2209 lines
42 KiB
Plaintext
2209 lines
42 KiB
Plaintext
|
[case testEmptyClass]
|
||
|
class Empty: pass
|
||
|
|
||
|
def f(e: Empty) -> Empty:
|
||
|
return e
|
||
|
[file driver.py]
|
||
|
from native import Empty, f
|
||
|
|
||
|
print(isinstance(Empty, type))
|
||
|
print(Empty)
|
||
|
print(str(Empty())[:20])
|
||
|
|
||
|
e = Empty()
|
||
|
print(f(e) is e)
|
||
|
[out]
|
||
|
True
|
||
|
<class 'native.Empty'>
|
||
|
<native.Empty object
|
||
|
True
|
||
|
|
||
|
[case testClassWithFields]
|
||
|
class C:
|
||
|
x: int
|
||
|
y: int
|
||
|
z: 'D'
|
||
|
class D:
|
||
|
pass
|
||
|
[file driver.py]
|
||
|
from native import C
|
||
|
from testutil import assertRaises
|
||
|
|
||
|
c = C()
|
||
|
assert not hasattr(c, 'x')
|
||
|
assert not hasattr(c, 'y')
|
||
|
c.x = 1
|
||
|
c.y = 2
|
||
|
print(c.x)
|
||
|
print(c.y)
|
||
|
c.x = 10**30
|
||
|
print(c.x)
|
||
|
c.x = 10**30+1
|
||
|
print(c.x)
|
||
|
assert hasattr(c, 'x')
|
||
|
assert hasattr(c, 'y')
|
||
|
assert not hasattr(c, 'z')
|
||
|
with assertRaises(AttributeError, "'C' object attribute 'x' cannot be deleted"):
|
||
|
del c.x
|
||
|
assert hasattr(c, 'x')
|
||
|
assert hasattr(c, 'y')
|
||
|
with assertRaises(AttributeError, "'C' object attribute 'y' cannot be deleted"):
|
||
|
del c.y
|
||
|
assert hasattr(c, 'y')
|
||
|
c.x = 10**30+2
|
||
|
print(c.x)
|
||
|
assert hasattr(c, 'x')
|
||
|
[out]
|
||
|
1
|
||
|
2
|
||
|
1000000000000000000000000000000
|
||
|
1000000000000000000000000000001
|
||
|
1000000000000000000000000000002
|
||
|
|
||
|
[case testClassWithDeletableAttributes]
|
||
|
from typing import Any, cast
|
||
|
from testutil import assertRaises
|
||
|
|
||
|
class C:
|
||
|
__deletable__ = ['x', 'y']
|
||
|
x: int
|
||
|
y: int
|
||
|
z: int
|
||
|
|
||
|
def test_delete() -> None:
|
||
|
c = C()
|
||
|
c.x = 1
|
||
|
c.y = 2
|
||
|
c.z = 3
|
||
|
del c.x
|
||
|
del c.y
|
||
|
assert c.z == 3
|
||
|
with assertRaises(AttributeError):
|
||
|
c.x
|
||
|
with assertRaises(AttributeError):
|
||
|
c.y
|
||
|
|
||
|
def test_delete_any() -> None:
|
||
|
c: Any = C()
|
||
|
c.x = 1
|
||
|
c.y = 2
|
||
|
c.z = 3
|
||
|
del c.x
|
||
|
del c.y
|
||
|
with assertRaises(AttributeError, "'C' object attribute 'z' cannot be deleted"):
|
||
|
del c.z
|
||
|
assert c.z == 3
|
||
|
with assertRaises(AttributeError):
|
||
|
c.x
|
||
|
with assertRaises(AttributeError):
|
||
|
c.y
|
||
|
|
||
|
class Base:
|
||
|
__deletable__ = ['a']
|
||
|
a: int
|
||
|
b: int
|
||
|
|
||
|
class Deriv(Base):
|
||
|
__deletable__ = ('c',)
|
||
|
c: str
|
||
|
d: str
|
||
|
|
||
|
def test_delete_with_inheritance() -> None:
|
||
|
d = Deriv()
|
||
|
d.a = 0
|
||
|
d.b = 1
|
||
|
d.c = 'X'
|
||
|
d.d = 'Y'
|
||
|
del d.a
|
||
|
with assertRaises(AttributeError):
|
||
|
d.a
|
||
|
del d.c
|
||
|
with assertRaises(AttributeError):
|
||
|
d.c
|
||
|
assert d.b == 1
|
||
|
assert d.d == 'Y'
|
||
|
|
||
|
def test_delete_with_inheritance_any() -> None:
|
||
|
d: Any = Deriv()
|
||
|
d.a = 0
|
||
|
d.b = 1
|
||
|
d.c = 'X'
|
||
|
d.d = 'Y'
|
||
|
del d.a
|
||
|
with assertRaises(AttributeError):
|
||
|
d.a
|
||
|
del d.c
|
||
|
with assertRaises(AttributeError):
|
||
|
d.c
|
||
|
with assertRaises(AttributeError):
|
||
|
del d.b
|
||
|
assert d.b == 1
|
||
|
with assertRaises(AttributeError):
|
||
|
del d.d
|
||
|
assert d.d == 'Y'
|
||
|
|
||
|
def decorator(cls):
|
||
|
return cls
|
||
|
|
||
|
@decorator
|
||
|
class NonExt:
|
||
|
x: int
|
||
|
y: int
|
||
|
|
||
|
# No effect in a non-native class
|
||
|
__deletable__ = ['x']
|
||
|
|
||
|
def test_non_ext() -> None:
|
||
|
n = NonExt()
|
||
|
n.x = 2
|
||
|
n.y = 3
|
||
|
del n.x
|
||
|
del n.y
|
||
|
with assertRaises(AttributeError):
|
||
|
n.x
|
||
|
with assertRaises(AttributeError):
|
||
|
n.y
|
||
|
|
||
|
def test_non_ext_any() -> None:
|
||
|
n: Any = NonExt()
|
||
|
n.x = 2
|
||
|
n.y = 3
|
||
|
del n.x
|
||
|
del n.y
|
||
|
with assertRaises(AttributeError):
|
||
|
n.x
|
||
|
with assertRaises(AttributeError):
|
||
|
n.y
|
||
|
|
||
|
[case testNonExtMisc]
|
||
|
from typing import Any, overload
|
||
|
|
||
|
def decorator(cls) -> Any:
|
||
|
return cls
|
||
|
|
||
|
@decorator
|
||
|
class C:
|
||
|
def __init__(self) -> None:
|
||
|
self.c = 3
|
||
|
|
||
|
def get_c(self) -> int:
|
||
|
return self.c
|
||
|
|
||
|
@decorator
|
||
|
class B(C):
|
||
|
def __init__(self) -> None:
|
||
|
super().__init__()
|
||
|
self.b = 2
|
||
|
|
||
|
def get_b(self) -> int:
|
||
|
return self.b
|
||
|
|
||
|
@decorator
|
||
|
class A(B):
|
||
|
def __init__(self) -> None:
|
||
|
super().__init__()
|
||
|
self.a = 1
|
||
|
|
||
|
@classmethod
|
||
|
def constant(cls) -> int:
|
||
|
return 4
|
||
|
|
||
|
def get_a(self) -> int:
|
||
|
return self.a
|
||
|
|
||
|
@decorator
|
||
|
class Overload:
|
||
|
@overload
|
||
|
def get(self, index: int) -> int: ...
|
||
|
|
||
|
@overload
|
||
|
def get(self, index: str) -> str: ...
|
||
|
|
||
|
def get(self, index: Any) -> Any:
|
||
|
return index
|
||
|
|
||
|
def get(c: Overload, s: str) -> str:
|
||
|
return c.get(s)
|
||
|
|
||
|
@decorator
|
||
|
class Var:
|
||
|
x = 'xy'
|
||
|
|
||
|
def get_class_var() -> str:
|
||
|
return Var.x
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import A, Overload, get, get_class_var
|
||
|
a = A()
|
||
|
assert a.a == 1
|
||
|
assert a.b == 2
|
||
|
assert a.c == 3
|
||
|
assert a.get_a() == 1
|
||
|
assert a.get_b() == 2
|
||
|
assert a.get_c() == 3
|
||
|
assert A.constant() == 4
|
||
|
|
||
|
o = Overload()
|
||
|
assert get(o, "test") == "test"
|
||
|
assert o.get(20) == 20
|
||
|
|
||
|
assert get_class_var() == 'xy'
|
||
|
|
||
|
[case testEnum]
|
||
|
from enum import Enum
|
||
|
|
||
|
class TestEnum(Enum):
|
||
|
_order_ = "a b"
|
||
|
a : int = 1
|
||
|
b : int = 2
|
||
|
|
||
|
@classmethod
|
||
|
def test(cls) -> int:
|
||
|
return 3
|
||
|
|
||
|
assert TestEnum.test() == 3
|
||
|
|
||
|
[file other.py]
|
||
|
# Force a multi-module test to make sure we can compile multi-file with
|
||
|
# non-extension classes
|
||
|
|
||
|
[file driver.py]
|
||
|
import sys
|
||
|
# "_order_" isn't supported in 3.5
|
||
|
if sys.version_info[:2] > (3, 5):
|
||
|
from native import TestEnum
|
||
|
assert TestEnum.a.name == 'a'
|
||
|
assert TestEnum.a.value == 1
|
||
|
assert TestEnum.b.name == 'b'
|
||
|
assert TestEnum.b.value == 2
|
||
|
|
||
|
[case testGetAttribute]
|
||
|
class C:
|
||
|
x: int
|
||
|
y: int
|
||
|
|
||
|
def getx(c: C) -> int:
|
||
|
return c.x
|
||
|
|
||
|
def gety(c: C) -> int:
|
||
|
return c.y
|
||
|
[file driver.py]
|
||
|
from native import C, getx, gety
|
||
|
c = C()
|
||
|
c.x = 10**30
|
||
|
c.y = 10**30 + 1
|
||
|
print(getx(c))
|
||
|
print(gety(c))
|
||
|
[out]
|
||
|
1000000000000000000000000000000
|
||
|
1000000000000000000000000000001
|
||
|
|
||
|
[case testSetAttribute]
|
||
|
class C:
|
||
|
x: int
|
||
|
y: int
|
||
|
|
||
|
def setx(c: C, v: int) -> None:
|
||
|
c.x = v
|
||
|
|
||
|
def sety(c: C, v: int) -> None:
|
||
|
c.y = v
|
||
|
[file driver.py]
|
||
|
from native import C, setx, sety
|
||
|
c = C()
|
||
|
setx(c, 10**30)
|
||
|
sety(c, 10**30 + 1)
|
||
|
print(c.x)
|
||
|
print(c.y)
|
||
|
setx(c, 4)
|
||
|
sety(c, 5)
|
||
|
print(c.x, c.y)
|
||
|
setx(c, 10**30 + 2)
|
||
|
sety(c, 10**30 + 3)
|
||
|
print(c.x)
|
||
|
print(c.y)
|
||
|
[out]
|
||
|
1000000000000000000000000000000
|
||
|
1000000000000000000000000000001
|
||
|
4 5
|
||
|
1000000000000000000000000000002
|
||
|
1000000000000000000000000000003
|
||
|
|
||
|
[case testAttributeTypes]
|
||
|
from typing import List, Tuple
|
||
|
class C:
|
||
|
a: List[int]
|
||
|
b: bool
|
||
|
c: C
|
||
|
d: object
|
||
|
e: int
|
||
|
|
||
|
def setattrs(o: C, a: List[int], b: bool, c: C) -> None:
|
||
|
o.a = a
|
||
|
o.b = b
|
||
|
o.c = c
|
||
|
|
||
|
def getattrs(o: C) -> Tuple[List[int], bool, C]:
|
||
|
return o.a, o.b, o.c
|
||
|
[file driver.py]
|
||
|
from native import C, setattrs, getattrs
|
||
|
from testutil import assertRaises
|
||
|
|
||
|
c1 = C()
|
||
|
c2 = C()
|
||
|
aa = [2]
|
||
|
setattrs(c1, aa, True, c2)
|
||
|
a, b, c = getattrs(c1)
|
||
|
assert a is aa
|
||
|
assert b is True
|
||
|
assert c is c2
|
||
|
|
||
|
o = object()
|
||
|
c1.d = o
|
||
|
assert c1.d is o
|
||
|
|
||
|
c3 = C()
|
||
|
with assertRaises(AttributeError, "attribute 'a' of 'C' undefined"):
|
||
|
c3.a
|
||
|
with assertRaises(AttributeError, "attribute 'b' of 'C' undefined"):
|
||
|
c3.b
|
||
|
with assertRaises(AttributeError, "attribute 'c' of 'C' undefined"):
|
||
|
c3.c
|
||
|
with assertRaises(AttributeError, "attribute 'd' of 'C' undefined"):
|
||
|
c3.d
|
||
|
with assertRaises(AttributeError, "attribute 'e' of 'C' undefined"):
|
||
|
c3.e
|
||
|
|
||
|
[case testInitMethodWithMissingNoneReturnAnnotation]
|
||
|
class C:
|
||
|
def __init__(self):
|
||
|
self.x = 42
|
||
|
[file driver.py]
|
||
|
from native import C
|
||
|
c = C()
|
||
|
assert c is not None
|
||
|
assert c.x == 42
|
||
|
|
||
|
[case testConstructClassWithDefaultConstructor]
|
||
|
class C:
|
||
|
a: int
|
||
|
b: int
|
||
|
|
||
|
def f() -> C:
|
||
|
c = C()
|
||
|
c.a = 13
|
||
|
return c
|
||
|
[file driver.py]
|
||
|
from native import f, C
|
||
|
c = f()
|
||
|
assert c.a == 13
|
||
|
assert type(c) == C
|
||
|
assert not hasattr(c, 'b')
|
||
|
|
||
|
[case testCastUserClass]
|
||
|
from typing import List
|
||
|
|
||
|
class C:
|
||
|
x: int
|
||
|
|
||
|
def f(a: List[C]) -> C:
|
||
|
return a[0]
|
||
|
[file driver.py]
|
||
|
from native import f, C
|
||
|
c = C()
|
||
|
assert f([c]) is c
|
||
|
|
||
|
[case testClass1]
|
||
|
class A:
|
||
|
def __init__(self, x: int) -> None:
|
||
|
self.x = x
|
||
|
def foo(self) -> int:
|
||
|
return self.x+1
|
||
|
def foo() -> int:
|
||
|
a = A(20)
|
||
|
return a.foo()
|
||
|
[file driver.py]
|
||
|
from native import A, foo
|
||
|
a = A(10)
|
||
|
assert a.foo() == 11
|
||
|
assert foo() == 21
|
||
|
|
||
|
[case testGenericClass]
|
||
|
from typing import TypeVar, Generic, Sequence
|
||
|
T = TypeVar('T')
|
||
|
class C(Generic[T]):
|
||
|
x: T
|
||
|
def __init__(self, x: T) -> None:
|
||
|
self.x = x
|
||
|
def get(self) -> T:
|
||
|
return self.x
|
||
|
def set(self, y: T) -> None:
|
||
|
self.x = y
|
||
|
|
||
|
# Test subclassing generic classes both with and without a generic param
|
||
|
class A(Sequence[int]):
|
||
|
pass
|
||
|
class B(Sequence[T]):
|
||
|
pass
|
||
|
|
||
|
def f(c: C[int]) -> int:
|
||
|
y = c.get()
|
||
|
d = C[int](2)
|
||
|
c.set(c.get() + 1 + d.get())
|
||
|
c.x = c.x + 2
|
||
|
return c.x
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import C, f
|
||
|
c = C(6)
|
||
|
assert f(c) == 11
|
||
|
c.x = 'x'
|
||
|
assert c.x == 'x'
|
||
|
c.set([1])
|
||
|
assert c.x == [1]
|
||
|
assert c.get() == [1]
|
||
|
|
||
|
[case testSubclass1]
|
||
|
from typing import Tuple
|
||
|
|
||
|
class A:
|
||
|
def __init__(self) -> None:
|
||
|
self.x = 10
|
||
|
def hi(self, suffix: str) -> str:
|
||
|
return str(self.x) + suffix
|
||
|
class B(A):
|
||
|
def __init__(self) -> None:
|
||
|
self.x = 20
|
||
|
self.y = 'world'
|
||
|
def hi(self, suffix: str) -> str:
|
||
|
return 'hello ' + str(self.y) + suffix
|
||
|
|
||
|
def use_a(x: A) -> Tuple[int, str]:
|
||
|
return (x.x, x.hi(''))
|
||
|
def use_b(x: B) -> str:
|
||
|
return x.hi('')
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import A, B, use_a, use_b
|
||
|
a = A()
|
||
|
b = B()
|
||
|
assert use_a(a) == (10, '10')
|
||
|
assert use_a(b) == (20, 'hello world')
|
||
|
assert a.x == 10
|
||
|
assert b.x == 20
|
||
|
assert b.y == 'world'
|
||
|
assert a.hi('!') == '10!'
|
||
|
assert b.hi('!') == 'hello world!'
|
||
|
assert use_b(b) == 'hello world'
|
||
|
|
||
|
[case testSubclassSpecialize1]
|
||
|
class A:
|
||
|
def foo(self, x: int) -> object:
|
||
|
print('A')
|
||
|
return str(x)
|
||
|
def bar(self, x: int) -> None:
|
||
|
print(x + 1)
|
||
|
class B(A):
|
||
|
def foo(self, x: object) -> int:
|
||
|
print('B')
|
||
|
return id(x)
|
||
|
def bar(self, x: object) -> None:
|
||
|
print(x)
|
||
|
|
||
|
def use_a(x: A, y: int) -> object:
|
||
|
x.bar(10)
|
||
|
return x.foo(y)
|
||
|
|
||
|
def use_b(x: B, y: object) -> int:
|
||
|
return x.foo(y)
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import A, B, use_a, use_b
|
||
|
a = A()
|
||
|
b = B()
|
||
|
o = object()
|
||
|
i = 10
|
||
|
assert a.foo(10) == '10'
|
||
|
assert b.foo(o) == id(o)
|
||
|
assert use_a(a, 10) == '10'
|
||
|
assert use_b(b, o) == id(o)
|
||
|
assert use_a(b, i) == id(i)
|
||
|
[out]
|
||
|
A
|
||
|
B
|
||
|
11
|
||
|
A
|
||
|
B
|
||
|
10
|
||
|
B
|
||
|
|
||
|
[case testSubclassSpecialize2]
|
||
|
class A:
|
||
|
def foo(self, x: int) -> object:
|
||
|
print('A')
|
||
|
return str(x)
|
||
|
class B(A):
|
||
|
def foo(self, x: object) -> object:
|
||
|
print('B')
|
||
|
return x
|
||
|
class C(B):
|
||
|
def foo(self, x: object) -> int:
|
||
|
print('C')
|
||
|
return id(x)
|
||
|
|
||
|
def use_a(x: A, y: int) -> object:
|
||
|
return x.foo(y)
|
||
|
|
||
|
def use_b(x: B, y: object) -> object:
|
||
|
return x.foo(y)
|
||
|
|
||
|
def use_c(x: C, y: object) -> int:
|
||
|
return x.foo(y)
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import A, B, C, use_a, use_b, use_c
|
||
|
a = A()
|
||
|
b = B()
|
||
|
c = C()
|
||
|
o = object()
|
||
|
i = 10
|
||
|
assert a.foo(10) == '10'
|
||
|
assert b.foo(o) == o
|
||
|
assert c.foo(o) == id(o)
|
||
|
assert use_a(a, 10) == '10'
|
||
|
assert use_a(b, i) is i
|
||
|
assert use_a(c, i) == id(i)
|
||
|
assert use_b(b, o) == o
|
||
|
assert use_b(c, o) == id(o)
|
||
|
assert use_c(c, o) == id(o)
|
||
|
[out]
|
||
|
A
|
||
|
B
|
||
|
C
|
||
|
A
|
||
|
B
|
||
|
C
|
||
|
B
|
||
|
C
|
||
|
C
|
||
|
|
||
|
[case testIsInstance]
|
||
|
from typing import Optional
|
||
|
class X: pass
|
||
|
class A(X): pass
|
||
|
class B(A): pass
|
||
|
|
||
|
def isa(x: object) -> bool:
|
||
|
return isinstance(x, A)
|
||
|
def isint(x: object) -> bool:
|
||
|
return isinstance(x, int)
|
||
|
def isstr(x: object) -> bool:
|
||
|
return isinstance(x, str)
|
||
|
def islist(x: object) -> bool:
|
||
|
return isinstance(x, list)
|
||
|
def ist(x: object, t: object) -> bool: # TODO: Second argument should be 'type'
|
||
|
return isinstance(x, t)
|
||
|
def pointless(x: Optional[X]) -> str:
|
||
|
if isinstance(x, A):
|
||
|
return str(x)
|
||
|
return ''
|
||
|
[file driver.py]
|
||
|
from native import X, A, B, isa, isint, isstr, islist, ist
|
||
|
assert isa(1) == False
|
||
|
assert isa(A()) == True
|
||
|
assert isa(B()) == True
|
||
|
assert isa(X()) == False
|
||
|
|
||
|
assert isint(1) == True
|
||
|
assert isint('') == False
|
||
|
assert isint(A()) == False
|
||
|
|
||
|
assert isstr(1) == False
|
||
|
assert isstr('') == True
|
||
|
|
||
|
assert islist(1) == False
|
||
|
assert islist([]) == True
|
||
|
|
||
|
assert ist(1, int) == True
|
||
|
assert ist(1, str) == False
|
||
|
try:
|
||
|
ist(1, 2)
|
||
|
except TypeError:
|
||
|
pass
|
||
|
else:
|
||
|
assert False
|
||
|
|
||
|
[case testSubclassUninitAttr]
|
||
|
class X:
|
||
|
x: int
|
||
|
class A(X):
|
||
|
pass
|
||
|
[file driver.py]
|
||
|
import traceback
|
||
|
from native import A
|
||
|
try:
|
||
|
A().x
|
||
|
except AttributeError:
|
||
|
traceback.print_exc()
|
||
|
[out]
|
||
|
Traceback (most recent call last):
|
||
|
File "driver.py", line 4, in <module>
|
||
|
A().x
|
||
|
AttributeError: attribute 'x' of 'X' undefined
|
||
|
|
||
|
[case testClassMethods]
|
||
|
MYPY = False
|
||
|
if MYPY:
|
||
|
from typing import ClassVar
|
||
|
class C:
|
||
|
lurr: 'ClassVar[int]' = 9
|
||
|
@staticmethod
|
||
|
def foo(x: int) -> int: return 10 + x
|
||
|
@classmethod
|
||
|
def bar(cls, x: int) -> int: return cls.lurr + x
|
||
|
@staticmethod
|
||
|
def baz(x: int, y: int = 10) -> int: return y - x
|
||
|
@classmethod
|
||
|
def quux(cls, x: int, y: int = 10) -> int: return y - x
|
||
|
|
||
|
class D(C):
|
||
|
def f(self) -> int:
|
||
|
return super().foo(1) + super().bar(2) + super().baz(10) + super().quux(10)
|
||
|
|
||
|
def test1() -> int:
|
||
|
return C.foo(1) + C.bar(2) + C.baz(10) + C.quux(10) + C.quux(y=10, x=9)
|
||
|
def test2() -> int:
|
||
|
c = C()
|
||
|
return c.foo(1) + c.bar(2) + c.baz(10)
|
||
|
[file driver.py]
|
||
|
from native import *
|
||
|
assert C.foo(10) == 20
|
||
|
assert C.bar(10) == 19
|
||
|
c = C()
|
||
|
assert c.foo(10) == 20
|
||
|
assert c.bar(10) == 19
|
||
|
|
||
|
assert test1() == 23
|
||
|
assert test2() == 22
|
||
|
|
||
|
d = D()
|
||
|
assert d.f() == 22
|
||
|
|
||
|
[case testSuper]
|
||
|
from mypy_extensions import trait
|
||
|
from typing import List
|
||
|
|
||
|
class A:
|
||
|
def __init__(self, x: int) -> None:
|
||
|
self.x = x
|
||
|
|
||
|
def foo(self, x: int) -> int:
|
||
|
return x
|
||
|
|
||
|
class B(A):
|
||
|
def __init__(self, x: int, y: int) -> None:
|
||
|
super().__init__(x)
|
||
|
self.y = y
|
||
|
|
||
|
def foo(self, x: int) -> int:
|
||
|
return super().foo(x+1)
|
||
|
|
||
|
class C(B):
|
||
|
def __init__(self, x: int, y: int) -> None:
|
||
|
super(C, self).__init__(x, y + 1)
|
||
|
|
||
|
def foo(self, x: int) -> int:
|
||
|
# should go to A, not B
|
||
|
return super(B, self).foo(x+1)
|
||
|
|
||
|
class X:
|
||
|
def __init__(self, x: int) -> None:
|
||
|
self.x = x
|
||
|
class Y(X):
|
||
|
pass
|
||
|
class Z(Y):
|
||
|
def __init__(self, x: int, y: int) -> None:
|
||
|
super().__init__(x)
|
||
|
self.y = y
|
||
|
|
||
|
@trait
|
||
|
class T:
|
||
|
def v_int(self, x: int) -> None: pass
|
||
|
def v_list(self, x: List[int]) -> None:
|
||
|
if x:
|
||
|
self.v_int(x[0])
|
||
|
self.v_list(x[1:])
|
||
|
|
||
|
class PrintList(T):
|
||
|
def v_int(self, x: int) -> None:
|
||
|
print(x)
|
||
|
def v_list(self, x: List[int]) -> None:
|
||
|
print('yo!')
|
||
|
super().v_list(x)
|
||
|
|
||
|
[file driver.py]
|
||
|
import traceback
|
||
|
from native import *
|
||
|
b = B(10, 20)
|
||
|
assert b.x == 10 and b.y == 20
|
||
|
c = C(10, 20)
|
||
|
assert c.x == 10 and c.y == 21
|
||
|
z = Z(10, 20)
|
||
|
assert z.x == 10 and z.y == 20
|
||
|
|
||
|
assert c.foo(10) == 11
|
||
|
|
||
|
PrintList().v_list([1,2,3])
|
||
|
[out]
|
||
|
yo!
|
||
|
1
|
||
|
yo!
|
||
|
2
|
||
|
yo!
|
||
|
3
|
||
|
yo!
|
||
|
|
||
|
[case testSubclassException]
|
||
|
class Failure(Exception):
|
||
|
def __init__(self, x: int) -> None:
|
||
|
self.x = x
|
||
|
|
||
|
def foo() -> None:
|
||
|
raise Failure(10)
|
||
|
|
||
|
def heyo() -> int:
|
||
|
try:
|
||
|
foo()
|
||
|
except Failure as e:
|
||
|
return e.x
|
||
|
return -1
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import foo, heyo, Failure
|
||
|
try:
|
||
|
foo()
|
||
|
except Failure as e:
|
||
|
assert str(e) == '10'
|
||
|
assert e.x == 10
|
||
|
heyo()
|
||
|
|
||
|
[case testSubclassDict]
|
||
|
from typing import Dict
|
||
|
class WelpDict(Dict[str, int]):
|
||
|
def __init__(self) -> None:
|
||
|
self.emarhavil = 3
|
||
|
def foo(self) -> int:
|
||
|
return self.emarhavil
|
||
|
|
||
|
def welp() -> int:
|
||
|
x = WelpDict()
|
||
|
x['a'] = 10
|
||
|
x['b'] = 15
|
||
|
x.emarhavil = 5
|
||
|
return x['a'] + x['b'] + x.emarhavil + x.foo()
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import welp
|
||
|
assert welp() == 35
|
||
|
|
||
|
[case testSubclassPy]
|
||
|
from b import B, V
|
||
|
class A(B):
|
||
|
def __init__(self, x: int, y: int) -> None:
|
||
|
super().__init__(y)
|
||
|
self.x = x
|
||
|
|
||
|
def foo(self, x: int) -> int:
|
||
|
print("hi", x)
|
||
|
return x+1
|
||
|
|
||
|
class C(V[int]):
|
||
|
def f(self) -> int: return 10
|
||
|
|
||
|
assert isinstance(C(), V)
|
||
|
|
||
|
def f(x: A) -> None:
|
||
|
print(x.x)
|
||
|
print(x.y)
|
||
|
print(x.foo(20))
|
||
|
|
||
|
[file b.py]
|
||
|
from typing import Generic, TypeVar
|
||
|
T = TypeVar('T')
|
||
|
|
||
|
class B:
|
||
|
def __init__(self, y: int) -> None:
|
||
|
self.y = y
|
||
|
def foo(self, x: int) -> int:
|
||
|
print("parent!")
|
||
|
return x + self.y
|
||
|
def bar(self) -> None:
|
||
|
print("hello!", self.y)
|
||
|
|
||
|
class V(Generic[T]):
|
||
|
def f(self) -> T:
|
||
|
raise Exception('unimplemented')
|
||
|
[file driver.py]
|
||
|
import native
|
||
|
a = native.A(10, 20)
|
||
|
a.foo(10)
|
||
|
a.bar()
|
||
|
native.f(a)
|
||
|
[out]
|
||
|
hi 10
|
||
|
hello! 20
|
||
|
10
|
||
|
20
|
||
|
hi 20
|
||
|
21
|
||
|
|
||
|
[case testDisallowSubclassFromPy]
|
||
|
# We'll want to allow this at some point but right now we need to
|
||
|
# disallow it because it doesn't work.
|
||
|
class A:
|
||
|
pass
|
||
|
[file b.py]
|
||
|
from native import A
|
||
|
|
||
|
# It would be better if we disallowed it at class decl time but it is
|
||
|
# really easy to do in __new__
|
||
|
|
||
|
class B(A):
|
||
|
pass
|
||
|
|
||
|
[file driver.py]
|
||
|
from b import B
|
||
|
try:
|
||
|
B()
|
||
|
except TypeError:
|
||
|
pass
|
||
|
else:
|
||
|
assert False, "instantiating was supposed to fail"
|
||
|
|
||
|
[case testClassVariable]
|
||
|
MYPY = False
|
||
|
if MYPY:
|
||
|
from typing import ClassVar
|
||
|
class A:
|
||
|
x = 10 # type: ClassVar[int]
|
||
|
|
||
|
def g(x: int) -> None:
|
||
|
A.x = 10
|
||
|
def f() -> int:
|
||
|
return A.x
|
||
|
[file driver.py]
|
||
|
from native import A, f
|
||
|
assert f() == 10
|
||
|
A.x = 200
|
||
|
assert f() == 200
|
||
|
|
||
|
[case testDefaultVars]
|
||
|
from typing import Optional
|
||
|
class A:
|
||
|
x = 10
|
||
|
w: object = 10
|
||
|
def lol(self) -> None:
|
||
|
self.x = 100
|
||
|
|
||
|
LOL = 'lol'
|
||
|
class B(A):
|
||
|
y = LOL
|
||
|
z = None # type: Optional[str]
|
||
|
b = True
|
||
|
bogus = None # type: int
|
||
|
|
||
|
def g() -> None:
|
||
|
a = A()
|
||
|
assert a.x == 10
|
||
|
a.x = 20
|
||
|
assert a.x == 20
|
||
|
b = B()
|
||
|
assert b.x == 10
|
||
|
b.x = 20
|
||
|
assert b.x == 20
|
||
|
assert b.y == 'lol'
|
||
|
b.y = 'rofl'
|
||
|
assert b.y == 'rofl'
|
||
|
assert b.z is None
|
||
|
[file driver.py]
|
||
|
from native import *
|
||
|
g()
|
||
|
|
||
|
a = A()
|
||
|
assert a.x == 10
|
||
|
a.x = 20
|
||
|
assert a.x == 20
|
||
|
b = B()
|
||
|
assert b.x == 10
|
||
|
b.x = 20
|
||
|
assert b.x == 20
|
||
|
assert b.y == 'lol'
|
||
|
b.y = 'rofl'
|
||
|
assert b.y == 'rofl'
|
||
|
assert b.z is None
|
||
|
# N.B: this doesn't match cpython
|
||
|
assert not hasattr(b, 'bogus')
|
||
|
|
||
|
[case testProtocol]
|
||
|
from typing_extensions import Protocol
|
||
|
|
||
|
class Proto(Protocol):
|
||
|
def foo(self, x: int) -> None:
|
||
|
pass
|
||
|
|
||
|
def bar(self, x: int) -> None:
|
||
|
pass
|
||
|
|
||
|
class A:
|
||
|
def foo(self, x: int) -> None:
|
||
|
print("A:", x)
|
||
|
|
||
|
def bar(self, *args: int, **kwargs: int) -> None:
|
||
|
print("A:", args, kwargs)
|
||
|
|
||
|
class B(A, Proto):
|
||
|
def foo(self, x: int) -> None:
|
||
|
print("B:", x)
|
||
|
|
||
|
def bar(self, *args: int, **kwargs: int) -> None:
|
||
|
print("B:", args, kwargs)
|
||
|
|
||
|
def f(x: Proto) -> None:
|
||
|
x.foo(20)
|
||
|
x.bar(x=20)
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import A, B, f
|
||
|
|
||
|
f(A())
|
||
|
f(B())
|
||
|
|
||
|
# ... this exploits a bug in glue methods to distinguish whether we
|
||
|
# are making a direct call or a pycall...
|
||
|
[out]
|
||
|
A: 20
|
||
|
A: () {'x': 20}
|
||
|
B: 20
|
||
|
B: (20,) {}
|
||
|
|
||
|
[case testMethodOverrideDefault1]
|
||
|
class A:
|
||
|
def foo(self, x: int) -> None:
|
||
|
pass
|
||
|
class B(A):
|
||
|
def foo(self, x: int, y: int = 10) -> None:
|
||
|
print(x, y)
|
||
|
|
||
|
def a(x: A) -> None:
|
||
|
x.foo(1)
|
||
|
def b(x: B) -> None:
|
||
|
x.foo(2)
|
||
|
x.foo(2, 3)
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import B, a, b
|
||
|
a(B())
|
||
|
b(B())
|
||
|
[out]
|
||
|
1 10
|
||
|
2 10
|
||
|
2 3
|
||
|
|
||
|
[case testMethodOverrideDefault2]
|
||
|
class A:
|
||
|
def foo(self, *, x: int = -1) -> None:
|
||
|
pass
|
||
|
def bar(self, *, x: int = -1, y: int = -1) -> None:
|
||
|
pass
|
||
|
def baz(self, x: int = -1) -> None:
|
||
|
pass
|
||
|
class B(A):
|
||
|
def foo(self, *, y: int = 0, x: int = 0) -> None:
|
||
|
print(x, y)
|
||
|
def bar(self, *, y: int = 0, x: int = 0) -> None:
|
||
|
print(x, y)
|
||
|
def baz(self, x: int = 0, *, y: int = 0) -> None:
|
||
|
print(x, y)
|
||
|
|
||
|
def a(x: A) -> None:
|
||
|
x.foo(x=1)
|
||
|
x.bar(x=1, y=2)
|
||
|
x.bar(x=2, y=1)
|
||
|
x.baz()
|
||
|
x.baz(1)
|
||
|
x.baz(x=2)
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import B, a
|
||
|
a(B())
|
||
|
[out]
|
||
|
1 0
|
||
|
1 2
|
||
|
2 1
|
||
|
0 0
|
||
|
1 0
|
||
|
2 0
|
||
|
|
||
|
[case testMethodOverrideDefault3]
|
||
|
class A:
|
||
|
@classmethod
|
||
|
def foo(cls, *, x: int = 0) -> None:
|
||
|
pass
|
||
|
@staticmethod
|
||
|
def bar(*, x: int = 0) -> None:
|
||
|
pass
|
||
|
@staticmethod
|
||
|
def baz() -> object:
|
||
|
pass
|
||
|
class B(A):
|
||
|
@classmethod
|
||
|
def foo(cls, *, y: int = 0, x: int = 0) -> None:
|
||
|
print(x, y)
|
||
|
print(cls.__name__) # type: ignore
|
||
|
@staticmethod
|
||
|
def bar(*, y: int = 0, x: int = 0) -> None:
|
||
|
print(x, y)
|
||
|
@staticmethod
|
||
|
def baz() -> int:
|
||
|
return 10
|
||
|
|
||
|
# This is just to make sure that this stuff works even when the
|
||
|
# methods might be overridden.
|
||
|
class C(B):
|
||
|
@classmethod
|
||
|
def foo(cls, *, y: int = 0, x: int = 0) -> None:
|
||
|
pass
|
||
|
@staticmethod
|
||
|
def bar(*, y: int = 0, x: int = 0) -> None:
|
||
|
pass
|
||
|
@staticmethod
|
||
|
def baz() -> int:
|
||
|
return 10
|
||
|
|
||
|
def a(x: A) -> None:
|
||
|
x.foo(x=1)
|
||
|
x.bar(x=1)
|
||
|
print(x.baz())
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import B, a
|
||
|
a(B())
|
||
|
[out]
|
||
|
1 0
|
||
|
B
|
||
|
1 0
|
||
|
10
|
||
|
|
||
|
[case testMethodOverrideDefault4]
|
||
|
class Foo:
|
||
|
def f(self, x: int=20, *, z: int=10) -> None:
|
||
|
pass
|
||
|
|
||
|
class Bar(Foo):
|
||
|
def f(self, *args: int, **kwargs: int) -> None:
|
||
|
print("stuff", args, kwargs)
|
||
|
|
||
|
z: Foo = Bar()
|
||
|
z.f(1, z=50)
|
||
|
z.f()
|
||
|
|
||
|
[out]
|
||
|
stuff (1,) {'z': 50}
|
||
|
stuff () {}
|
||
|
|
||
|
[case testMethodOverrideDefault5]
|
||
|
from testutil import make_python_function
|
||
|
from mypy_extensions import mypyc_attr
|
||
|
from typing import TypeVar, Any
|
||
|
|
||
|
@mypyc_attr(allow_interpreted_subclasses=True)
|
||
|
class Foo:
|
||
|
def f(self, x: int=20, *, z: int=10) -> None:
|
||
|
print("Foo", x, z)
|
||
|
|
||
|
@make_python_function
|
||
|
def baz_f(self: Any, *args: int, **kwargs: int) -> None:
|
||
|
print("Baz", args, kwargs)
|
||
|
|
||
|
# Make an "interpreted" subtype of Foo
|
||
|
type2: Any = type
|
||
|
Bar = type2('Bar', (Foo,), {})
|
||
|
Baz = type2('Baz', (Foo,), {'f': baz_f})
|
||
|
|
||
|
y: Foo = Bar()
|
||
|
y.f(1, z=2)
|
||
|
y.f()
|
||
|
|
||
|
z: Foo = Baz()
|
||
|
z.f(1, z=2)
|
||
|
z.f()
|
||
|
|
||
|
[out]
|
||
|
Foo 1 2
|
||
|
Foo 20 10
|
||
|
Baz (1,) {'z': 2}
|
||
|
Baz () {}
|
||
|
|
||
|
[case testMethodOverrideDefault6]
|
||
|
from typing import Optional
|
||
|
|
||
|
class Foo:
|
||
|
def f(self, x: int=20) -> None:
|
||
|
pass
|
||
|
|
||
|
class Bar(Foo):
|
||
|
def f(self, x: Optional[int]=None) -> None:
|
||
|
print(x)
|
||
|
|
||
|
z: Foo = Bar()
|
||
|
z.f(1)
|
||
|
z.f()
|
||
|
|
||
|
[out]
|
||
|
1
|
||
|
None
|
||
|
|
||
|
[case testMethodOverrideDefault7]
|
||
|
from typing import TypeVar, Any
|
||
|
|
||
|
class Foo:
|
||
|
def f(self, x: int, *args: int, **kwargs: int) -> None:
|
||
|
print("Foo", x, args, kwargs)
|
||
|
|
||
|
class Bar(Foo):
|
||
|
def f(self, *args: int, **kwargs: int) -> None:
|
||
|
print("Bar", args, kwargs)
|
||
|
|
||
|
z: Foo = Bar()
|
||
|
z.f(1, z=2)
|
||
|
z.f(1, 2, 3)
|
||
|
# z.f(x=5) # Not tested because we (knowingly) do the wrong thing and pass it as positional
|
||
|
|
||
|
[out]
|
||
|
Bar (1,) {'z': 2}
|
||
|
Bar (1, 2, 3) {}
|
||
|
--Bar () {'x': 5}
|
||
|
|
||
|
[case testMethodOverrideDefault8]
|
||
|
from typing import TypeVar, Any
|
||
|
|
||
|
class Foo:
|
||
|
def f(self, *args: int, **kwargs: int) -> None:
|
||
|
print("Foo", args, kwargs)
|
||
|
|
||
|
class Bar(Foo):
|
||
|
def f(self, x: int = 10, *args: int, **kwargs: int) -> None:
|
||
|
print("Bar", x, args, kwargs)
|
||
|
|
||
|
z: Foo = Bar()
|
||
|
z.f(1, z=2)
|
||
|
z.f(1, 2, 3)
|
||
|
z.f()
|
||
|
|
||
|
[out]
|
||
|
Bar 1 () {'z': 2}
|
||
|
Bar 1 (2, 3) {}
|
||
|
Bar 10 () {}
|
||
|
|
||
|
[case testMethodOverrideDefault9]
|
||
|
from testutil import make_python_function
|
||
|
from mypy_extensions import mypyc_attr
|
||
|
from typing import TypeVar, Any
|
||
|
|
||
|
@mypyc_attr(allow_interpreted_subclasses=True)
|
||
|
class Foo:
|
||
|
def f(self, x: int=20, y: int=40) -> None:
|
||
|
print("Foo", x, y)
|
||
|
|
||
|
# This sort of argument renaming is dodgy and not really sound but we
|
||
|
# shouldn't break it when they aren't actually used by name...
|
||
|
# (They *ought* to be positional only!)
|
||
|
@make_python_function
|
||
|
def baz_f(self, a: int=30, y: int=50) -> None:
|
||
|
print("Baz", a, y)
|
||
|
|
||
|
# Make an "interpreted" subtype of Foo
|
||
|
type2: Any = type
|
||
|
Baz = type2('Baz', (Foo,), {'f': baz_f})
|
||
|
|
||
|
z: Foo = Baz()
|
||
|
z.f()
|
||
|
z.f(y=1)
|
||
|
z.f(1, 2)
|
||
|
# Not tested because we don't (and probably won't) match cpython here
|
||
|
# from testutil import assertRaises
|
||
|
# with assertRaises(TypeError):
|
||
|
# z.f(x=7)
|
||
|
|
||
|
[out]
|
||
|
Baz 30 50
|
||
|
Baz 30 1
|
||
|
Baz 1 2
|
||
|
|
||
|
[case testOverride]
|
||
|
class A:
|
||
|
def f(self) -> int:
|
||
|
return 0
|
||
|
def g(self) -> int:
|
||
|
return 1
|
||
|
|
||
|
class B(A):
|
||
|
def g(self) -> int:
|
||
|
return 2
|
||
|
|
||
|
class C(B):
|
||
|
def f(self) -> int:
|
||
|
return 3
|
||
|
|
||
|
def test() -> None:
|
||
|
ba: A = B()
|
||
|
ca: A = C()
|
||
|
assert ba.f() == 0
|
||
|
assert ba.g() == 2
|
||
|
assert ca.f() == 3
|
||
|
assert ca.g() == 2
|
||
|
cc = C()
|
||
|
assert cc.f() == 3
|
||
|
assert cc.g() == 2
|
||
|
print('ok')
|
||
|
[file driver.py]
|
||
|
import native
|
||
|
native.test()
|
||
|
[out]
|
||
|
ok
|
||
|
|
||
|
[case testNoMetaclass]
|
||
|
from foo import Base
|
||
|
|
||
|
class Nothing(Base): # type: ignore
|
||
|
pass
|
||
|
|
||
|
[file foo.py]
|
||
|
from typing import Any
|
||
|
class Meta(type):
|
||
|
pass
|
||
|
|
||
|
class _Base(metaclass=Meta):
|
||
|
pass
|
||
|
Base = _Base # type: Any
|
||
|
|
||
|
[file driver.py]
|
||
|
try:
|
||
|
import native
|
||
|
except TypeError as e:
|
||
|
assert(str(e) == "mypyc classes can't have a metaclass")
|
||
|
|
||
|
[case testMetaclass]
|
||
|
from meta import Meta
|
||
|
import six
|
||
|
|
||
|
class Nothing1(metaclass=Meta):
|
||
|
pass
|
||
|
|
||
|
def ident(x): return x
|
||
|
|
||
|
@ident
|
||
|
class Test:
|
||
|
pass
|
||
|
|
||
|
class Nothing2(six.with_metaclass(Meta, Test)):
|
||
|
pass
|
||
|
|
||
|
@six.add_metaclass(Meta)
|
||
|
class Nothing3:
|
||
|
pass
|
||
|
|
||
|
[file meta.py]
|
||
|
from typing import Any
|
||
|
class Meta(type):
|
||
|
def __new__(mcs, name, bases, dct):
|
||
|
dct['X'] = 10
|
||
|
return super().__new__(mcs, name, bases, dct)
|
||
|
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import Nothing1, Nothing2, Nothing3
|
||
|
assert Nothing1.X == 10
|
||
|
assert Nothing2.X == 10
|
||
|
assert Nothing3.X == 10
|
||
|
|
||
|
[case testPickling]
|
||
|
from mypy_extensions import trait, mypyc_attr
|
||
|
from typing import Any, TypeVar, Generic
|
||
|
|
||
|
def dec(x: Any) -> Any:
|
||
|
return x
|
||
|
|
||
|
@mypyc_attr(allow_interpreted_subclasses=True)
|
||
|
class A:
|
||
|
x: int
|
||
|
y: str
|
||
|
|
||
|
@mypyc_attr(allow_interpreted_subclasses=True)
|
||
|
class B(A):
|
||
|
z: bool
|
||
|
|
||
|
def __init__(self, x: int, y: str, z: bool) -> None:
|
||
|
self.x = x
|
||
|
self.y = y
|
||
|
self.z = z
|
||
|
|
||
|
@trait
|
||
|
class T:
|
||
|
a: str
|
||
|
|
||
|
class C(B, T):
|
||
|
w: object
|
||
|
|
||
|
# property shouldn't go in
|
||
|
@property
|
||
|
def foo(self) -> int:
|
||
|
return 0
|
||
|
|
||
|
@dec
|
||
|
class D:
|
||
|
x: int
|
||
|
|
||
|
class E(D):
|
||
|
y: int
|
||
|
|
||
|
|
||
|
U = TypeVar('U')
|
||
|
|
||
|
class F(Generic[U]):
|
||
|
y: int
|
||
|
|
||
|
class G(F[int]):
|
||
|
pass
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import A, B, T, C, D, E, F, G
|
||
|
|
||
|
import copy
|
||
|
import pickle
|
||
|
|
||
|
assert A.__mypyc_attrs__ == ('x', 'y')
|
||
|
assert B.__mypyc_attrs__ == ('z', 'x', 'y')
|
||
|
assert T.__mypyc_attrs__ == ('a',)
|
||
|
assert C.__mypyc_attrs__ == ('w', 'z', 'x', 'y', 'a')
|
||
|
assert not hasattr(D, '__mypyc_attrs__')
|
||
|
assert E.__mypyc_attrs__ == ('y', '__dict__')
|
||
|
assert F.__mypyc_attrs__ == ('y', '__dict__')
|
||
|
assert G.__mypyc_attrs__ == ('y', '__dict__')
|
||
|
|
||
|
b = B(10, '20', False)
|
||
|
assert b.__getstate__() == {'z': False, 'x': 10, 'y': '20'}
|
||
|
b2 = copy.copy(b)
|
||
|
assert b is not b2 and b.y == b2.y
|
||
|
|
||
|
b3 = pickle.loads(pickle.dumps(b))
|
||
|
assert b is not b3 and b.y == b3.y
|
||
|
|
||
|
e = E()
|
||
|
e.x = 10
|
||
|
e.y = 20
|
||
|
|
||
|
assert e.__getstate__() == {'y': 20, '__dict__': {'x': 10}}
|
||
|
e2 = pickle.loads(pickle.dumps(e))
|
||
|
assert e is not e2 and e.x == e2.x and e.y == e2.y
|
||
|
|
||
|
|
||
|
[case testInterpretedParentInit]
|
||
|
from interp import C
|
||
|
from typing import TypeVar
|
||
|
|
||
|
T = TypeVar('T')
|
||
|
|
||
|
def dec(x: T) -> T:
|
||
|
return x
|
||
|
|
||
|
@dec
|
||
|
class A:
|
||
|
def __init__(self, x: int) -> None:
|
||
|
self.x = x
|
||
|
|
||
|
class B(A):
|
||
|
s = 'test'
|
||
|
|
||
|
def b(x: int) -> B: return B(x)
|
||
|
|
||
|
class D(C):
|
||
|
s = 'test'
|
||
|
|
||
|
def d(x: int) -> D: return D(x)
|
||
|
|
||
|
|
||
|
[file interp.py]
|
||
|
class C:
|
||
|
def __init__(self, x: int) -> None:
|
||
|
self.x = x
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import b, d, B, D
|
||
|
|
||
|
def test(f, v):
|
||
|
x = f(v)
|
||
|
assert x.x == v
|
||
|
assert x.s == 'test'
|
||
|
|
||
|
test(b, 20)
|
||
|
test(d, 30)
|
||
|
test(B, -1)
|
||
|
test(D, -2)
|
||
|
|
||
|
[case testInterpretedInherit]
|
||
|
from typing import TypeVar, Any, overload
|
||
|
from mypy_extensions import mypyc_attr, trait
|
||
|
|
||
|
T = TypeVar('T')
|
||
|
def dec(x: T) -> T: return x
|
||
|
|
||
|
@mypyc_attr(allow_interpreted_subclasses=True)
|
||
|
class Top:
|
||
|
def spam(self) -> str:
|
||
|
return "grandparent"
|
||
|
|
||
|
@mypyc_attr(allow_interpreted_subclasses=True)
|
||
|
@trait
|
||
|
class Trait:
|
||
|
def trait_method(self) -> str:
|
||
|
return "trait"
|
||
|
|
||
|
@mypyc_attr(allow_interpreted_subclasses=True)
|
||
|
class Foo(Top, Trait):
|
||
|
def __init__(self, x: int) -> None:
|
||
|
self.x = x
|
||
|
|
||
|
def foo(self) -> str:
|
||
|
return "parent foo: " + self.bar(self.x)
|
||
|
|
||
|
def bar(self, x: int) -> str:
|
||
|
return "parent bar: {}".format(x + self.x)
|
||
|
|
||
|
@dec
|
||
|
def decorated(self) -> str:
|
||
|
return "decorated parent"
|
||
|
|
||
|
@property
|
||
|
def read_property(self) -> str:
|
||
|
return "parent prop"
|
||
|
|
||
|
@overload
|
||
|
def overloaded(self, index: int) -> int: ...
|
||
|
|
||
|
@overload
|
||
|
def overloaded(self, index: str) -> str: ...
|
||
|
|
||
|
def overloaded(self, index: Any) -> Any:
|
||
|
return index
|
||
|
|
||
|
def foo(x: Foo) -> str:
|
||
|
return x.foo()
|
||
|
|
||
|
def bar(x: Foo, y: int) -> str:
|
||
|
return x.bar(y)
|
||
|
|
||
|
def spam(x: Top) -> str:
|
||
|
return x.spam()
|
||
|
|
||
|
def decorated(x: Foo) -> str:
|
||
|
return x.decorated()
|
||
|
|
||
|
def prop(x: Foo) -> str:
|
||
|
return x.read_property
|
||
|
|
||
|
def trait_method(x: Trait) -> str:
|
||
|
return x.trait_method()
|
||
|
|
||
|
def overloaded(x: Foo, s: str) -> str:
|
||
|
return x.overloaded(s)
|
||
|
|
||
|
[file interp.py]
|
||
|
from typing import Any
|
||
|
from native import Foo
|
||
|
|
||
|
class Bar(Foo):
|
||
|
def bar(self, x: int) -> str:
|
||
|
return "child bar: {}".format(x + self.x)
|
||
|
|
||
|
def spam(self) -> str:
|
||
|
assert super().spam() == "grandparent"
|
||
|
return "child"
|
||
|
|
||
|
@property
|
||
|
def read_property(self) -> str:
|
||
|
return "child prop"
|
||
|
|
||
|
def decorated(self) -> str:
|
||
|
return "decorated child"
|
||
|
|
||
|
def trait_method(self) -> str:
|
||
|
return "child"
|
||
|
|
||
|
def overloaded(self, index: Any) -> Any:
|
||
|
return index + index
|
||
|
|
||
|
|
||
|
class InterpBase:
|
||
|
def eggs(self) -> str:
|
||
|
return "eggs"
|
||
|
|
||
|
class Baz(InterpBase, Bar):
|
||
|
def __init__(self) -> None:
|
||
|
super().__init__(1000)
|
||
|
self.z = self.read_property
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import Foo, foo, bar, spam, decorated, overloaded, prop, trait_method
|
||
|
from interp import Bar, Baz
|
||
|
from unittest.mock import patch
|
||
|
from testutil import assertRaises
|
||
|
|
||
|
x = Foo(10)
|
||
|
y = Bar(20)
|
||
|
z = Baz()
|
||
|
|
||
|
assert isinstance(y, Bar)
|
||
|
assert y.x == 20
|
||
|
assert y.bar(10) == "child bar: 30"
|
||
|
assert y.foo() == "parent foo: child bar: 40"
|
||
|
assert foo(y) == "parent foo: child bar: 40"
|
||
|
assert bar(y, 30) == "child bar: 50"
|
||
|
y.x = 30
|
||
|
assert bar(y, 30) == "child bar: 60"
|
||
|
|
||
|
assert spam(y) == "child"
|
||
|
assert y.read_property == "child prop"
|
||
|
assert prop(x) == "parent prop"
|
||
|
assert prop(y) == "child prop"
|
||
|
assert y.decorated() == "decorated child"
|
||
|
assert decorated(y) == "decorated child"
|
||
|
assert y.overloaded("test") == "testtest"
|
||
|
assert overloaded(y, "test") == "testtest"
|
||
|
|
||
|
assert y.trait_method() == "child"
|
||
|
assert trait_method(y) == "child"
|
||
|
|
||
|
assert z.bar(10) == "child bar: 1010"
|
||
|
assert bar(z, 10) == "child bar: 1010"
|
||
|
assert z.z == "child prop"
|
||
|
assert z.eggs() == "eggs"
|
||
|
|
||
|
with patch("interp.Bar.spam", lambda self: "monkey patched"):
|
||
|
assert y.spam() == "monkey patched"
|
||
|
spam(y) == "monkey patched"
|
||
|
|
||
|
with patch("interp.Bar.spam", lambda self: 20):
|
||
|
assert y.spam() == 20
|
||
|
with assertRaises(TypeError, "str object expected; got int"):
|
||
|
spam(y)
|
||
|
|
||
|
with assertRaises(TypeError, "int object expected; got str"):
|
||
|
y.x = "test"
|
||
|
|
||
|
[case testProperty]
|
||
|
from typing import Callable
|
||
|
from mypy_extensions import trait
|
||
|
class Temperature:
|
||
|
@property
|
||
|
def celsius(self) -> float:
|
||
|
return 5.0 * (self.fahrenheit - 32.0) / 9.0
|
||
|
|
||
|
def __init__(self, fahrenheit: float) -> None:
|
||
|
self.fahrenheit = fahrenheit
|
||
|
|
||
|
def print_temp(self) -> None:
|
||
|
print("F:", self.fahrenheit, "C:", self.celsius)
|
||
|
|
||
|
@property
|
||
|
def rankine(self) -> float:
|
||
|
raise NotImplementedError
|
||
|
|
||
|
class Access:
|
||
|
@property
|
||
|
def number_of_accesses(self) -> int:
|
||
|
self._count += 1
|
||
|
return self._count
|
||
|
def __init__(self) -> None:
|
||
|
self._count = 0
|
||
|
|
||
|
from typing import Callable
|
||
|
class BaseProperty:
|
||
|
@property
|
||
|
def doc(self) -> str:
|
||
|
return "Represents a sequence of values. Updates itself by next, which is a new value."
|
||
|
|
||
|
@property
|
||
|
def value(self) -> object:
|
||
|
return self._incrementer
|
||
|
|
||
|
@property
|
||
|
def bad_value(self) -> object:
|
||
|
return self._incrementer
|
||
|
|
||
|
@property
|
||
|
def next(self) -> BaseProperty:
|
||
|
return BaseProperty(self._incrementer + 1)
|
||
|
|
||
|
def __init__(self, value: int) -> None:
|
||
|
self._incrementer = value
|
||
|
|
||
|
class DerivedProperty(BaseProperty):
|
||
|
@property
|
||
|
def value(self) -> int:
|
||
|
return self._incrementer
|
||
|
|
||
|
@property
|
||
|
def bad_value(self) -> object:
|
||
|
return self._incrementer
|
||
|
|
||
|
def __init__(self, incr_func: Callable[[int], int], value: int) -> None:
|
||
|
BaseProperty.__init__(self, value)
|
||
|
self._incr_func = incr_func
|
||
|
|
||
|
@property
|
||
|
def next(self) -> DerivedProperty:
|
||
|
return DerivedProperty(self._incr_func, self._incr_func(self.value))
|
||
|
|
||
|
class AgainProperty(DerivedProperty):
|
||
|
@property
|
||
|
def next(self) -> AgainProperty:
|
||
|
return AgainProperty(self._incr_func, self._incr_func(self._incr_func(self.value)))
|
||
|
|
||
|
@property
|
||
|
def bad_value(self) -> int:
|
||
|
return self._incrementer
|
||
|
|
||
|
def print_first_n(n: int, thing: BaseProperty) -> None:
|
||
|
vals = []
|
||
|
cur_thing = thing
|
||
|
for _ in range(n):
|
||
|
vals.append(cur_thing.value)
|
||
|
cur_thing = cur_thing.next
|
||
|
print ('', vals)
|
||
|
|
||
|
@trait
|
||
|
class Trait:
|
||
|
@property
|
||
|
def value(self) -> int:
|
||
|
return 3
|
||
|
|
||
|
class Printer(Trait):
|
||
|
def print_value(self) -> None:
|
||
|
print(self.value)
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import Temperature, Access
|
||
|
import traceback
|
||
|
x = Temperature(32.0)
|
||
|
try:
|
||
|
print (x.rankine)
|
||
|
except NotImplementedError as e:
|
||
|
traceback.print_exc()
|
||
|
print (x.celsius)
|
||
|
x.print_temp()
|
||
|
|
||
|
y = Temperature(212.0)
|
||
|
print (y.celsius)
|
||
|
y.print_temp()
|
||
|
|
||
|
z = Access()
|
||
|
print (z.number_of_accesses)
|
||
|
print (z.number_of_accesses)
|
||
|
print (z.number_of_accesses)
|
||
|
print (z.number_of_accesses)
|
||
|
|
||
|
from native import BaseProperty, DerivedProperty, AgainProperty, print_first_n
|
||
|
a = BaseProperty(7)
|
||
|
b = DerivedProperty((lambda x: x // 2 if (x % 2 == 0) else 3 * x + 1), 7)
|
||
|
c = AgainProperty((lambda x: x // 2 if (x % 2 == 0) else 3 * x + 1), 7)
|
||
|
|
||
|
def py_print_first_n(n: int, thing: BaseProperty) -> None:
|
||
|
vals = []
|
||
|
cur_thing = thing
|
||
|
for _ in range(n):
|
||
|
vals.append(cur_thing.value)
|
||
|
cur_thing = cur_thing.next
|
||
|
print ('', vals)
|
||
|
|
||
|
py_print_first_n(20, a)
|
||
|
py_print_first_n(20, b)
|
||
|
py_print_first_n(20, c)
|
||
|
|
||
|
print(a.next.next.next.bad_value)
|
||
|
print(b.next.next.next.bad_value)
|
||
|
print(c.next.next.next.bad_value)
|
||
|
|
||
|
print_first_n(20, a)
|
||
|
print_first_n(20, b)
|
||
|
print_first_n(20, c)
|
||
|
|
||
|
print (a.doc)
|
||
|
print (b.doc)
|
||
|
print (c.doc)
|
||
|
|
||
|
from native import Printer
|
||
|
Printer().print_value()
|
||
|
print (Printer().value)
|
||
|
[out]
|
||
|
Traceback (most recent call last):
|
||
|
File "driver.py", line 5, in <module>
|
||
|
print (x.rankine)
|
||
|
File "native.py", line 16, in rankine
|
||
|
raise NotImplementedError
|
||
|
NotImplementedError
|
||
|
0.0
|
||
|
F: 32.0 C: 0.0
|
||
|
100.0
|
||
|
F: 212.0 C: 100.0
|
||
|
1
|
||
|
2
|
||
|
3
|
||
|
4
|
||
|
[7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]
|
||
|
[7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1, 4, 2, 1]
|
||
|
[7, 11, 17, 26, 40, 10, 16, 4, 1, 2, 4, 1, 2, 4, 1, 2, 4, 1, 2, 4]
|
||
|
10
|
||
|
34
|
||
|
26
|
||
|
[7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]
|
||
|
[7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1, 4, 2, 1]
|
||
|
[7, 11, 17, 26, 40, 10, 16, 4, 1, 2, 4, 1, 2, 4, 1, 2, 4, 1, 2, 4]
|
||
|
Represents a sequence of values. Updates itself by next, which is a new value.
|
||
|
Represents a sequence of values. Updates itself by next, which is a new value.
|
||
|
Represents a sequence of values. Updates itself by next, which is a new value.
|
||
|
3
|
||
|
3
|
||
|
|
||
|
[case testPropertySetters]
|
||
|
|
||
|
from mypy_extensions import trait
|
||
|
|
||
|
class Foo():
|
||
|
def __init__(self) -> None:
|
||
|
self.attr = "unmodified"
|
||
|
|
||
|
class A:
|
||
|
def __init__(self) -> None:
|
||
|
self._x = 0
|
||
|
self._foo = Foo()
|
||
|
|
||
|
@property
|
||
|
def x(self) -> int:
|
||
|
return self._x
|
||
|
|
||
|
@x.setter
|
||
|
def x(self, val : int) -> None:
|
||
|
self._x = val
|
||
|
|
||
|
@property
|
||
|
def foo(self) -> Foo:
|
||
|
return self._foo
|
||
|
|
||
|
@foo.setter
|
||
|
def foo(self, val : Foo) -> None:
|
||
|
self._foo = val
|
||
|
|
||
|
# Overrides base property setters and getters
|
||
|
class B(A):
|
||
|
def __init__(self) -> None:
|
||
|
self._x = 10
|
||
|
|
||
|
@property
|
||
|
def x(self) -> int:
|
||
|
return self._x + 1
|
||
|
|
||
|
@x.setter
|
||
|
def x(self, val : int) -> None:
|
||
|
self._x = val + 1
|
||
|
|
||
|
# Inherits base property setters and getters
|
||
|
class C(A):
|
||
|
def __init__(self) -> None:
|
||
|
A.__init__(self)
|
||
|
|
||
|
@trait
|
||
|
class D():
|
||
|
def __init__(self) -> None:
|
||
|
self._x = 0
|
||
|
|
||
|
@property
|
||
|
def x(self) -> int:
|
||
|
return self._x
|
||
|
|
||
|
@x.setter
|
||
|
def x(self, val : int) -> None:
|
||
|
self._x = val
|
||
|
|
||
|
#Inherits trait property setters and getters
|
||
|
class E(D):
|
||
|
def __init__(self) -> None:
|
||
|
D.__init__(self)
|
||
|
|
||
|
#Overrides trait property setters and getters
|
||
|
class F(D):
|
||
|
def __init__(self) -> None:
|
||
|
self._x = 10
|
||
|
|
||
|
@property
|
||
|
def x(self) -> int:
|
||
|
return self._x + 10
|
||
|
|
||
|
@x.setter
|
||
|
def x(self, val : int) -> None:
|
||
|
self._x = val + 10
|
||
|
|
||
|
# # Property setter and getter are subtypes of base property setters and getters
|
||
|
# # class G(A):
|
||
|
# # def __init__(self) -> None:
|
||
|
# # A.__init__(self)
|
||
|
|
||
|
# # @property
|
||
|
# # def y(self) -> int:
|
||
|
# # return self._y
|
||
|
|
||
|
# # @y.setter
|
||
|
# # def y(self, val : object) -> None:
|
||
|
# # self._y = val
|
||
|
|
||
|
# No inheritance, just plain setter/getter
|
||
|
class G:
|
||
|
def __init__(self, x: int) -> None:
|
||
|
self._x = x
|
||
|
|
||
|
@property
|
||
|
def x(self) -> int:
|
||
|
return self._x
|
||
|
|
||
|
@x.setter
|
||
|
def x(self, x: int) -> None:
|
||
|
self._x = x
|
||
|
|
||
|
class H:
|
||
|
def __init__(self, g: G) -> None:
|
||
|
self.g = g
|
||
|
self.g.x = 5 # Should not be treated as initialization
|
||
|
|
||
|
[file other.py]
|
||
|
# Run in both interpreted and compiled mode
|
||
|
|
||
|
from native import A, B, C, D, E, F, G
|
||
|
|
||
|
a = A()
|
||
|
assert a.x == 0
|
||
|
assert a._x == 0
|
||
|
a.x = 1
|
||
|
assert a.x == 1
|
||
|
assert a._x == 1
|
||
|
a._x = 0
|
||
|
assert a.x == 0
|
||
|
assert a._x == 0
|
||
|
b = B()
|
||
|
assert b.x == 11
|
||
|
assert b._x == 10
|
||
|
b.x = 11
|
||
|
assert b.x == 13
|
||
|
b._x = 11
|
||
|
assert b.x == 12
|
||
|
c = C()
|
||
|
assert c.x == 0
|
||
|
c.x = 1000
|
||
|
assert c.x == 1000
|
||
|
e = E()
|
||
|
assert e.x == 0
|
||
|
e.x = 1000
|
||
|
assert e.x == 1000
|
||
|
f = F()
|
||
|
assert f.x == 20
|
||
|
f.x = 30
|
||
|
assert f.x == 50
|
||
|
g = G(4)
|
||
|
g.x = 20
|
||
|
assert g.x == 20
|
||
|
|
||
|
[file driver.py]
|
||
|
# Run the tests in both interpreted and compiled mode
|
||
|
import other
|
||
|
import other_interpreted
|
||
|
|
||
|
[out]
|
||
|
|
||
|
[case testSubclassAttributeAccess]
|
||
|
from mypy_extensions import trait
|
||
|
|
||
|
class A:
|
||
|
v = 0
|
||
|
|
||
|
class B(A):
|
||
|
v = 1
|
||
|
|
||
|
class C(B):
|
||
|
v = 2
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import A, B, C
|
||
|
|
||
|
a = A()
|
||
|
b = B()
|
||
|
c = C()
|
||
|
|
||
|
[case testCopyAlwaysDefinedAttributes]
|
||
|
import copy
|
||
|
from typing import Union
|
||
|
|
||
|
class A: pass
|
||
|
|
||
|
class C:
|
||
|
def __init__(self, n: int = 0) -> None:
|
||
|
self.n = n
|
||
|
self.s = ""
|
||
|
self.t = ("", 0)
|
||
|
self.u: Union[str, bytes] = ''
|
||
|
self.a = A()
|
||
|
|
||
|
def test_copy() -> None:
|
||
|
c1 = C()
|
||
|
c1.n = 1
|
||
|
c1.s = "x"
|
||
|
c2 = copy.copy(c1)
|
||
|
assert c2.n == 1
|
||
|
assert c2.s == "x"
|
||
|
assert c2.t == ("", 0)
|
||
|
assert c2.u == ''
|
||
|
assert c2.a is c1.a
|
||
|
|
||
|
[case testNonNativeCallsToDunderNewAndInit]
|
||
|
from typing import Any
|
||
|
from testutil import assertRaises
|
||
|
|
||
|
count_c = 0
|
||
|
|
||
|
class C:
|
||
|
def __init__(self) -> None:
|
||
|
self.x = 'a' # Always defined attribute
|
||
|
global count_c
|
||
|
count_c += 1
|
||
|
|
||
|
def get(self) -> str:
|
||
|
return self.x
|
||
|
|
||
|
def test_no_init_args() -> None:
|
||
|
global count_c
|
||
|
count_c = 0
|
||
|
|
||
|
# Use Any to get non-native semantics
|
||
|
cls: Any = C
|
||
|
# __new__ implicitly calls __init__ for native classes
|
||
|
obj = cls.__new__(cls)
|
||
|
assert obj.get() == 'a'
|
||
|
assert count_c == 1
|
||
|
# Make sure we don't call __init__ twice
|
||
|
obj2 = cls()
|
||
|
assert obj2.get() == 'a'
|
||
|
assert count_c == 2
|
||
|
|
||
|
count_d = 0
|
||
|
|
||
|
class D:
|
||
|
def __init__(self, x: str) -> None:
|
||
|
self.x = x # Always defined attribute
|
||
|
global count_d
|
||
|
count_d += 1
|
||
|
|
||
|
def get(self) -> str:
|
||
|
return self.x
|
||
|
|
||
|
def test_init_arg() -> None:
|
||
|
global count_d
|
||
|
count_d = 0
|
||
|
|
||
|
# Use Any to get non-native semantics
|
||
|
cls: Any = D
|
||
|
# __new__ implicitly calls __init__ for native classes
|
||
|
obj = cls.__new__(cls, 'abc')
|
||
|
assert obj.get() == 'abc'
|
||
|
assert count_d == 1
|
||
|
# Make sure we don't call __init__ twice
|
||
|
obj2 = cls('x')
|
||
|
assert obj2.get() == 'x'
|
||
|
assert count_d == 2
|
||
|
# Keyword args should work
|
||
|
obj = cls.__new__(cls, x='abc')
|
||
|
assert obj.get() == 'abc'
|
||
|
assert count_d == 3
|
||
|
|
||
|
def test_invalid_init_args() -> None:
|
||
|
# Use Any to get non-native semantics
|
||
|
cls: Any = D
|
||
|
with assertRaises(TypeError):
|
||
|
cls()
|
||
|
with assertRaises(TypeError):
|
||
|
cls(y='x')
|
||
|
with assertRaises(TypeError):
|
||
|
cls(1)
|
||
|
|
||
|
[case testTryDeletingAlwaysDefinedAttribute]
|
||
|
from typing import Any
|
||
|
from testutil import assertRaises
|
||
|
|
||
|
class C:
|
||
|
def __init__(self) -> None:
|
||
|
self.x = 0
|
||
|
|
||
|
class D(C):
|
||
|
pass
|
||
|
|
||
|
def test_try_deleting_always_defined_attr() -> None:
|
||
|
c: Any = C()
|
||
|
with assertRaises(AttributeError):
|
||
|
del c.x
|
||
|
d: Any = D()
|
||
|
with assertRaises(AttributeError):
|
||
|
del d.x
|
||
|
|
||
|
[case testAlwaysDefinedAttributeAndAllowInterpretedSubclasses]
|
||
|
from mypy_extensions import mypyc_attr
|
||
|
|
||
|
from m import define_interpreted_subclass
|
||
|
|
||
|
@mypyc_attr(allow_interpreted_subclasses=True)
|
||
|
class Base:
|
||
|
x = 5
|
||
|
y: int
|
||
|
def __init__(self, s: str) -> None:
|
||
|
self.s = s
|
||
|
|
||
|
class DerivedNative(Base):
|
||
|
def __init__(self) -> None:
|
||
|
super().__init__('x')
|
||
|
self.z = 3
|
||
|
|
||
|
def test_native_subclass() -> None:
|
||
|
o = DerivedNative()
|
||
|
assert o.x == 5
|
||
|
assert o.s == 'x'
|
||
|
assert o.z == 3
|
||
|
|
||
|
def test_interpreted_subclass() -> None:
|
||
|
define_interpreted_subclass(Base)
|
||
|
|
||
|
[file m.py]
|
||
|
from testutil import assertRaises
|
||
|
|
||
|
def define_interpreted_subclass(b):
|
||
|
class DerivedInterpreted1(b):
|
||
|
def __init__(self):
|
||
|
# Don't call base class __init__
|
||
|
pass
|
||
|
d1 = DerivedInterpreted1()
|
||
|
assert d1.x == 5
|
||
|
with assertRaises(AttributeError):
|
||
|
d1.y
|
||
|
with assertRaises(AttributeError):
|
||
|
d1.s
|
||
|
with assertRaises(AttributeError):
|
||
|
del d1.x
|
||
|
|
||
|
class DerivedInterpreted1(b):
|
||
|
def __init__(self):
|
||
|
super().__init__('y')
|
||
|
d2 = DerivedInterpreted1()
|
||
|
assert d2.x == 5
|
||
|
assert d2.s == 'y'
|
||
|
with assertRaises(AttributeError):
|
||
|
d2.y
|
||
|
with assertRaises(AttributeError):
|
||
|
del d2.x
|
||
|
|
||
|
[case testBaseClassSometimesDefinesAttribute]
|
||
|
class C:
|
||
|
def __init__(self, b: bool) -> None:
|
||
|
if b:
|
||
|
self.x = [1]
|
||
|
|
||
|
class D(C):
|
||
|
def __init__(self, b: bool) -> None:
|
||
|
super().__init__(b)
|
||
|
self.x = [2]
|
||
|
|
||
|
def test_base_class() -> None:
|
||
|
c = C(True)
|
||
|
assert c.x == [1]
|
||
|
c = C(False)
|
||
|
try:
|
||
|
c.x
|
||
|
except AttributeError:
|
||
|
return
|
||
|
assert False
|
||
|
|
||
|
def test_subclass() -> None:
|
||
|
d = D(True)
|
||
|
assert d.x == [2]
|
||
|
d = D(False)
|
||
|
assert d.x == [2]
|
||
|
|
||
|
[case testSerializableClass]
|
||
|
from mypy_extensions import mypyc_attr
|
||
|
from typing import Any
|
||
|
import copy
|
||
|
from testutil import assertRaises
|
||
|
|
||
|
@mypyc_attr(serializable=True)
|
||
|
class Base:
|
||
|
def __init__(self, s: str) -> None:
|
||
|
self.s = s
|
||
|
|
||
|
class Derived(Base):
|
||
|
def __init__(self, s: str, n: int) -> None:
|
||
|
super().__init__(s)
|
||
|
self.n = n
|
||
|
|
||
|
def test_copy_base() -> None:
|
||
|
o = Base('xyz')
|
||
|
o2 = copy.copy(o)
|
||
|
assert isinstance(o2, Base)
|
||
|
assert o2 is not o
|
||
|
assert o2.s == 'xyz'
|
||
|
|
||
|
def test_copy_derived() -> None:
|
||
|
d = Derived('xyz', 5)
|
||
|
d2 = copy.copy(d)
|
||
|
assert isinstance(d2, Derived)
|
||
|
assert d2 is not d
|
||
|
assert d2.s == 'xyz'
|
||
|
assert d2.n == 5
|
||
|
|
||
|
class NonSerializable:
|
||
|
def __init__(self, s: str) -> None:
|
||
|
self.s = s
|
||
|
|
||
|
@mypyc_attr(serializable=True)
|
||
|
class SerializableSub(NonSerializable):
|
||
|
def __init__(self, s: str, n: int) -> None:
|
||
|
super().__init__(s)
|
||
|
self.n = n
|
||
|
|
||
|
def test_serializable_sub_class() -> None:
|
||
|
n = NonSerializable('xyz')
|
||
|
assert n.s == 'xyz'
|
||
|
|
||
|
with assertRaises(TypeError):
|
||
|
copy.copy(n)
|
||
|
|
||
|
s = SerializableSub('foo', 6)
|
||
|
s2 = copy.copy(s)
|
||
|
assert s2 is not s
|
||
|
assert s2.s == 'foo'
|
||
|
assert s2.n == 6
|
||
|
|
||
|
def test_serializable_sub_class_call_new() -> None:
|
||
|
t: Any = SerializableSub
|
||
|
sub: SerializableSub = t.__new__(t)
|
||
|
with assertRaises(AttributeError):
|
||
|
sub.s
|
||
|
with assertRaises(AttributeError):
|
||
|
sub.n
|
||
|
base: NonSerializable = sub
|
||
|
with assertRaises(AttributeError):
|
||
|
base.s
|