blob: 2eb4dc261e99ab525e1370c6b3cd0fc3342c8213 [file]
[case testEmptyClass]
class Empty: pass
def f(e: Empty) -> Empty:
return e
class EmptyEllipsis: ...
def g(e: EmptyEllipsis) -> EmptyEllipsis:
return e
[file driver.py]
from native import Empty, EmptyEllipsis, f, g
print(isinstance(Empty, type))
print(Empty)
print(str(Empty())[:20])
e = Empty()
print(f(e) is e)
print(isinstance(EmptyEllipsis, type))
print(EmptyEllipsis)
print(str(EmptyEllipsis())[:28])
e2 = EmptyEllipsis()
print(g(e2) is e2)
[out]
True
<class 'native.Empty'>
<native.Empty object
True
True
<class 'native.EmptyEllipsis'>
<native.EmptyEllipsis 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 testTypedDictWithFields]
import collections
import json
from typing import TypedDict
class C(TypedDict):
x: collections.deque
spam: json.JSONDecoder
[file driver.py]
from native import C
from collections import deque
from json import JSONDecoder
print(C.__annotations__["x"] is deque)
print(C.__annotations__["spam"] is JSONDecoder)
[typing fixtures/typing-full.pyi]
[out]
True
True
[case testTypedDictWithFieldsAs]
import json as _json
from typing import TypedDict
class C(TypedDict):
spam: _json.JSONDecoder
[file driver.py]
from native import C
from json import JSONDecoder
print(C.__annotations__["spam"] is JSONDecoder)
[typing fixtures/typing-full.pyi]
[out]
True
[case testTypedDictWithFieldsFrom]
from json import decoder
from typing import TypedDict
class C(TypedDict):
spam: decoder.JSONDecoder
[file driver.py]
from native import C
from json import JSONDecoder
print(C.__annotations__["spam"] is JSONDecoder)
[typing fixtures/typing-full.pyi]
[out]
True
[case testNonExtClassAnnotationOfClassNotCreatedYet]
from __future__ import annotations
from mypy_extensions import mypyc_attr
@mypyc_attr(native_class=False)
class Node:
parent: Node
later: Later
class Later:
pass
[file driver.py]
from native import Node
assert Node.__annotations__ == {"parent": "native.Node", "later": "native.Later"}
[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, "attribute 'x' of 'C' undefined"):
c.x
with assertRaises(AttributeError, "attribute 'y' of 'C' undefined"):
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 testNonExtClassDecoratorReturningNonClass]
from functools import cache
from typing import Any, Callable, TypeVar, cast
from mypy_extensions import mypyc_attr
T = TypeVar("T")
@cache
class Group:
size = 10
def __init__(self, d: int) -> None:
self.d = d
def order(self) -> int:
return self.d * 2
def same(self, other: Group) -> bool:
return self is other
def make(d: int) -> Group:
return Group(d)
def order(g: Group) -> int:
return g.order()
@mypyc_attr(native_class=False)
class Base:
def __init__(self, x: int) -> None:
self.x = x
@cache
class Derived(Base):
def __init__(self, x: int) -> None:
super().__init__(x)
self.y = x + 1
def singleton(cls: Callable[[], T]) -> Callable[[], T]:
instances: list[T] = []
def get() -> T:
if not instances:
instances.append(cls())
return instances[0]
return get
@singleton
class Config:
def __init__(self) -> None:
self.value = 5
def get_value(self) -> int:
return self.value
def replace_class(cls: Any) -> Any:
# Return a new class with the same namespace, like six.add_metaclass does
ns = {k: v for k, v in cls.__dict__.items() if k not in ("__dict__", "__weakref__")}
return type(cls)(cls.__name__, cls.__bases__, ns)
@replace_class
class Replaced:
def __init__(self, v: int) -> None:
self.v = v
def get(self) -> int:
return self.v
def make_replaced(v: int) -> Replaced:
return Replaced(v)
def test_cache() -> None:
g = make(3)
assert g.order() == 6
assert order(g) == 6
assert g is make(3)
assert g is Group(3)
assert g is not make(4)
assert g.same(Group(3))
assert Group.size == 10
assert type(Group).__name__ == "_lru_cache_wrapper"
assert cast(Any, Group).cache_info().currsize == 2
def test_super() -> None:
d = Derived(4)
assert d.x == 4
assert d.y == 5
assert d is Derived(4)
def test_singleton() -> None:
c = Config()
assert c.get_value() == 5
assert c is Config()
def test_decorator_returning_class() -> None:
r = make_replaced(5)
assert r.get() == 5
assert type(r) is Replaced
[case testEnum]
from enum import Enum
class TestEnum(Enum):
_order_ = "a b"
a = 1
b = 2
@classmethod
def test(cls) -> int:
return 3
assert TestEnum.test() == 3
import enum
class Pokemon(enum.Enum):
magikarp = 1
squirtle = 2
slowbro = 3
assert Pokemon.magikarp.value == 1
assert Pokemon.squirtle.name == 'squirtle'
[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 testRunSuperYieldFromDict]
from typing import Any, Iterator
class DictSubclass(dict):
def items(self) -> Iterator[Any]:
yield 1
yield from super().items()
def test_sub_dict() -> None:
assert list(DictSubclass().items()) == [1]
[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 testClassKwargs]
class X:
def __init__(self, msg: str, **variables: int) -> None:
self.msg = msg
self.variables = variables
[file driver.py]
import traceback
from native import X
x = X('hello', a=0, b=1)
assert x.msg == 'hello'
assert x.variables == {'a': 0, 'b': 1}
try:
X('hello', msg='hello')
except TypeError as e:
print(f"{type(e).__name__}: {e}")
[out]
TypeError: argument for __init__() given by name ('msg') and position (1)
[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 testNonNativeGenericClass]
from typing import Dict, Generic, TypeVar
from mypy_extensions import mypyc_attr
T = TypeVar("T")
S = TypeVar("S")
@mypyc_attr(native_class=False)
class G(Generic[T]):
def __init__(self, x: T) -> None:
self.x = x
@mypyc_attr(native_class=False)
class Known:
pass
@mypyc_attr(native_class=False)
class GenericFirst(Generic[T], Known):
pass
@mypyc_attr(native_class=False)
class GenericLast(Known, Generic[T]):
pass
@mypyc_attr(native_class=False)
class Sub(G[int]):
pass
@mypyc_attr(native_class=False)
class ImplicitGeneric(G[T]):
pass
@mypyc_attr(native_class=False)
class TwoParams(G[T], Generic[S, T]):
pass
@mypyc_attr(native_class=False)
class DictSub(Dict[str, int]):
pass
def make() -> G[int]:
return G[int](5)
[file driver.py]
from typing import Generic
from native import (
G, T, S, Known, GenericFirst, GenericLast, Sub, ImplicitGeneric, TwoParams, DictSub, make
)
assert G.__bases__ == (Generic,)
assert G.__orig_bases__ == (Generic[T],)
assert G.__parameters__ == (T,)
assert G[int](3).x == 3
assert G[int].__origin__ is G
assert make().x == 5
assert GenericFirst.__bases__ == (Generic, Known)
assert GenericLast.__bases__ == (Known, Generic)
assert GenericFirst.__parameters__ == GenericLast.__parameters__ == (T,)
assert Sub.__bases__ == (G,)
assert Sub.__orig_bases__ == (G[int],)
assert Sub.__parameters__ == ()
assert Sub(7).x == 7
assert ImplicitGeneric.__parameters__ == (T,)
assert ImplicitGeneric[str]("a").x == "a"
assert TwoParams.__bases__ == (G, Generic)
assert TwoParams.__parameters__ == (S, T)
assert DictSub.__bases__ == (dict, Generic)
assert DictSub(a=1) == {"a": 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]
from typing import ClassVar, Any, final
from mypy_extensions import mypyc_attr
from interp import make_interpreted_subclass
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
@classmethod
def call_other(cls, x: int) -> int:
return cls.quux(x, 3)
class D(C):
def f(self) -> int:
return super().foo(1) + super().bar(2) + super().baz(10) + super().quux(10)
def ctest1() -> int:
return C.foo(1) + C.bar(2) + C.baz(10) + C.quux(10) + C.quux(y=10, x=9)
def ctest2() -> int:
c = C()
return c.foo(1) + c.bar(2) + c.baz(10)
CAny: Any = C
def test_classmethod_using_any() -> None:
assert CAny.foo(10) == 20
assert CAny.bar(10) == 19
def test_classmethod_on_instance() -> None:
c = C()
assert c.foo(10) == 20
assert c.bar(10) == 19
assert c.call_other(1) == 2
def test_classmethod_misc() -> None:
assert ctest1() == 23
assert ctest2() == 22
assert C.call_other(2) == 1
def test_classmethod_using_super() -> None:
d = D()
assert d.f() == 22
@final
class F1:
@classmethod
def f(cls, x: int) -> int:
return cls.g(x)
@classmethod
def g(cls, x: int) -> int:
return x + 1
class F2: # Implicitly final (no subclasses)
@classmethod
def f(cls, x: int) -> int:
return cls.g(x)
@classmethod
def g(cls, x: int) -> int:
return x + 1
def test_classmethod_of_final_class() -> None:
assert F1.f(5) == 6
assert F2.f(7) == 8
@mypyc_attr(allow_interpreted_subclasses=True)
class CI:
@classmethod
def f(cls, x: int) -> int:
return cls.g(x)
@classmethod
def g(cls, x: int) -> int:
return x + 1
def test_classmethod_with_allow_interpreted() -> None:
assert CI.f(4) == 5
sub = make_interpreted_subclass(CI)
assert sub.f(4) == 7
[file interp.py]
def make_interpreted_subclass(base):
class Sub(base): # type: ignore[misc, valid-type]
@classmethod
def g(cls, x: int) -> int:
return x + 3
return Sub
[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 testSuperInClassMethod]
from typing import Any
from mypy_extensions import trait
class A:
@classmethod
def name(cls) -> str:
return cls.__name__
@classmethod
def tagged(cls, tag: str, n: int = 1) -> str:
return f"{tag}{n}:{cls.__name__}"
@staticmethod
def static(x: int) -> int:
return x + 1
def method(self) -> str:
return "A.method:" + type(self).__name__
class B(A):
@classmethod
def name(cls) -> str:
return "B." + super().name()
@classmethod
def tagged(cls, tag: str, n: int = 1) -> str:
return "B." + super().tagged(tag, n=n + 1)
@classmethod
def two_arg(cls) -> str:
return "B." + super(B, cls).name()
@classmethod
def call_static(cls, x: int) -> int:
return super().static(x) + 10
@classmethod
def call_method(cls, b: "B") -> str:
# An instance method looked up through the class isn't bound
return "B." + super().method(b)
def from_instance(self) -> str:
return "B." + super().name() + "+" + super(B, self).name()
class C(B):
@classmethod
def name(cls) -> str:
return "C." + super().name()
def test_super_in_classmethod() -> None:
assert B.name() == "B.B"
assert C.name() == "C.B.C"
assert C().name() == "C.B.C"
assert B.tagged("x") == "B.x2:B"
assert C.tagged("x", 5) == "B.x6:C"
assert B.two_arg() == "B.B"
assert C.two_arg() == "B.C"
assert C.call_static(1) == 12
assert C.call_method(C()) == "B.A.method:C"
assert B().from_instance() == "B.B+B"
assert C().from_instance() == "B.C+C"
@trait
class T:
@classmethod
def describe(cls) -> str:
return "T:" + cls.__name__
class D(T):
@classmethod
def describe(cls) -> str:
return "D." + super().describe()
class E(D):
pass
def test_super_in_classmethod_with_trait() -> None:
assert D.describe() == "D.T:D"
assert E.describe() == "D.T:E"
init_subclass_log: list[str] = []
class Hooked:
def __init_subclass__(cls, **kwargs: Any) -> None:
super().__init_subclass__(**kwargs)
init_subclass_log.append("Hooked:" + cls.__name__)
class HookedChild(Hooked):
def __init_subclass__(cls, **kwargs: Any) -> None:
super().__init_subclass__(**kwargs)
init_subclass_log.append("HookedChild:" + cls.__name__)
class HookedGrandchild(HookedChild):
pass
def test_super_in_init_subclass() -> None:
assert init_subclass_log == [
"Hooked:HookedChild",
"Hooked:HookedGrandchild",
"HookedChild:HookedGrandchild",
]
class New(A):
made_by: str
def __new__(cls) -> "New":
obj = super().__new__(cls)
obj.made_by = super().name()
return obj
class NewChild(New):
pass
def test_super_in_dunder_new() -> None:
assert New().made_by == "New"
assert NewChild().made_by == "NewChild"
[case testSuperInGeneratorAndNestedFunction]
import asyncio
from typing import Callable, Iterator
class A:
@classmethod
def name(cls) -> str:
return cls.__name__
@classmethod
async def async_name(cls) -> str:
return "A.async_name:" + cls.__name__
def method(self) -> str:
return "A.method:" + type(self).__name__
class B(A):
@classmethod
def gen_cls(cls) -> Iterator[str]:
yield super().name()
def gen(self) -> Iterator[str]:
yield super().name()
yield super(B, self).name()
yield super().method()
@classmethod
async def async_name(cls) -> str:
return "B." + await super().async_name() + "+" + super().name()
async def coro(self) -> str:
return await super().async_name() + "+" + super().name()
def nested(self) -> list[str]:
def two_arg() -> str:
return super(B, self).method() + "+" + super(B, self).name()
# Zero-argument super() uses the first argument of the nested function
def zero_arg(b: B) -> str:
return super().method() + "+" + super().name()
f: Callable[[], str] = lambda: super(B, self).method()
return [two_arg(), zero_arg(self), f()]
@classmethod
def nested_cls(cls) -> list[str]:
def zero_arg(c: type[B]) -> str:
return super().name()
f: Callable[[], str] = lambda: super(B, cls).name()
return [zero_arg(cls), f()]
class C(B):
pass
def test_super_in_generator() -> None:
assert list(B.gen_cls()) == ["B"]
assert list(C.gen_cls()) == ["C"]
assert list(C().gen()) == ["C", "C", "A.method:C"]
def test_super_in_coroutine() -> None:
assert asyncio.run(B.async_name()) == "B.A.async_name:B+B"
assert asyncio.run(C.async_name()) == "B.A.async_name:C+C"
assert asyncio.run(C().coro()) == "A.async_name:C+C"
def test_super_in_nested_function() -> None:
assert C().nested() == ["A.method:C+C", "A.method:C+C", "A.method:C"]
assert C.nested_cls() == ["C", "C"]
[file asyncio/__init__.pyi]
def run(x: object) -> object: ...
[case testSuperSelfReassignedInArguments]
from typing import Iterator
class A:
def __init__(self, label: str) -> None:
self.label = label
def method(self, unused: int) -> str:
return self.label
@classmethod
def name(cls, unused: int) -> str:
return cls.__name__
class B(A):
# super() binds self before the arguments are evaluated
def method(self, unused: int) -> str:
def change_receiver() -> int:
nonlocal self
self = C("replacement")
return 0
return super().method(change_receiver())
def two_arg(self) -> str:
def change_receiver() -> int:
nonlocal self
self = C("replacement")
return 0
return super(B, self).method(change_receiver())
def call_classmethod(self) -> str:
def change_receiver() -> int:
nonlocal self
self = C("replacement")
return 0
return super().name(change_receiver())
def walrus(self) -> str:
return super().method(len((self := C("replacement")).label))
def walrus_keyword(self) -> str:
return super().method(unused=len((self := C("replacement")).label))
def walrus_classmethod(self) -> str:
return super().name(len((self := C("replacement")).label))
def gen(self) -> Iterator[str]:
yield super().method(len((self := C("replacement")).label))
yield self.label
def reassigned_before_call(self) -> str:
def change_receiver() -> None:
nonlocal self
self = C("replacement")
change_receiver()
return super().method(0)
class C(B):
pass
def test_super_self_reassigned_in_arguments() -> None:
b = B("original")
assert b.method(0) == "original"
assert b.two_arg() == "original"
assert b.call_classmethod() == "B"
assert b.walrus() == "original"
assert b.walrus_keyword() == "original"
assert b.walrus_classmethod() == "B"
assert list(b.gen()) == ["original", "replacement"]
def test_super_self_reassigned_before_call() -> None:
assert B("original").reassigned_before_call() == "replacement"
[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 testSubclassExceptionNoAttributeLeak]
# Regression test: on Python <3.12, a mypyc-compiled Exception subclass with
# instance attributes used to leak its __dict__ contents on every destruction
# because BaseException's inherited dealloc/clear doesn't know about the extra
# __dict__ slot mypyc appends at tp_dictoffset = sizeof(PyBaseExceptionObject).
class LeakyErr(Exception):
def __init__(self, payload: object) -> None:
self.data = payload
[file driver.py]
import gc
from native import LeakyErr
class Payload:
live = 0
def __init__(self) -> None:
Payload.live += 1
def __del__(self) -> None:
Payload.live -= 1
# Each iteration creates a fresh Payload, stashes it on a fresh LeakyErr, and
# drops both. If the exception's __dict__ leaks, the Payload it references
# survives — __del__ never runs and Payload.live grows without bound.
for _ in range(1000):
LeakyErr(Payload())
gc.collect()
assert Payload.live == 0, f"leaked {Payload.live} payloads"
# Sanity-check the normal raise/catch path still works and attrs are readable.
try:
raise LeakyErr("hi")
except LeakyErr as e:
assert e.data == "hi"
[case testInheritedHeapBaseFinalizerRuns]
# Regression test: when a mypyc-compiled Exception subclass inherits from an
# interpreted (heap) base that defines __del__, the base's finalizer must still
# run. Our custom tp_dealloc bypasses subtype_dealloc, so we must invoke
# PyObject_CallFinalizerFromDealloc ourselves when tp_finalize is inherited.
from mypy_extensions import mypyc_attr
events: list[str] = []
@mypyc_attr(native_class=False)
class HeapError(Exception):
def __del__(self) -> None:
events.append("base del")
class NativeError(HeapError):
def __init__(self, payload: object) -> None:
self.own = payload
[file driver.py]
import gc
from native import NativeError, events
for _ in range(1):
NativeError(object())
gc.collect()
assert events == ["base del"], events
[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 testSubclassUnsupportedException]
from mypy_extensions import mypyc_attr
@mypyc_attr(native_class=False)
class MyError(ZeroDivisionError):
pass
@mypyc_attr(native_class=False)
class MyError2(ZeroDivisionError):
def __init__(self, s: str) -> None:
super().__init__(s + "!")
self.x = s.upper()
def f() -> None:
raise MyError("foobar")
def test_non_native_exception_subclass_basics() -> None:
e = MyError()
assert isinstance(e, MyError)
assert isinstance(e, ZeroDivisionError)
assert isinstance(e, Exception)
e = MyError("x")
assert repr(e) == "MyError('x')"
e2 = MyError2("ab")
assert repr(e2) == "MyError2('ab!')", repr(e2)
assert e2.x == "AB"
def test_raise_non_native_exception_subclass_1() -> None:
try:
f()
except MyError:
x = True
else:
assert False
assert x
def test_raise_non_native_exception_subclass_2() -> None:
try:
f()
except ZeroDivisionError:
x = True
else:
assert False
assert x
[case testSubclassUnknownPythonBase]
# The base class is unknown during compilation, so it has type Any
from mypy_extensions import mypyc_attr
from typing import Any, Generic, TypeVar
from unchecked import Base, MetaBase, SecondBase, StrBase # type: ignore
T = TypeVar("T")
@mypyc_attr(native_class=False)
class C(Base):
def __init__(self, x: int) -> None:
super().__init__()
self.x = x
def marker(self) -> str:
return "override " + super().marker()
@mypyc_attr(native_class=False)
class InheritedInit(Base):
pass
@mypyc_attr(native_class=False)
class D(InheritedInit):
pass
@mypyc_attr(native_class=False)
class S(StrBase):
pass
@mypyc_attr(native_class=False)
class WithMeta(MetaBase):
pass
@mypyc_attr(native_class=False)
class Known:
def marker(self) -> str:
return "known"
@mypyc_attr(native_class=False)
class KnownFirst(Known, Base):
pass
@mypyc_attr(native_class=False)
class UnknownFirst(Base, Known):
pass
@mypyc_attr(native_class=False)
class TwoUnknown(Base, SecondBase):
pass
@mypyc_attr(native_class=False)
class GenericLast(Base, Generic[T]):
pass
@mypyc_attr(native_class=False)
class GenericFirst(Generic[T], Base):
pass
@mypyc_attr(native_class=False)
class KnownGeneric(Generic[T]):
pass
@mypyc_attr(native_class=False)
class KnownGenericBase(Base, KnownGeneric[int]):
pass
def test_unknown_base() -> None:
c = C(1)
assert c.x == 1
assert c.y == 2
assert c.label == "base attribute"
assert c.marker() == "override base"
c.z = 3
assert c.z == 3
assert c.__dict__ == {"x": 1, "y": 2, "z": 3}
assert isinstance(c, Base)
def test_inherited_init() -> None:
assert InheritedInit().y == 2
assert InheritedInit(7).y == 7
assert InheritedInit(x=11).y == 11
d = D(13)
assert d.y == 13
assert d.label == "base attribute"
assert d.marker() == "base"
assert D(x=17).y == 17
try:
InheritedInit(-1)
except ValueError as e:
assert str(e) == "negative"
else:
assert False
def test_unknown_extension_base() -> None:
assert S.__basicsize__ >= StrBase.__basicsize__
c = S("abc")
assert isinstance(c, str)
assert c == "abc"
assert len(c) == 3
assert c.upper() == "ABC"
def test_unknown_base_metaclass() -> None:
assert isinstance(WithMeta, type(MetaBase))
assert isinstance(WithMeta(), MetaBase)
def test_multiple_inheritance() -> None:
assert KnownFirst.__bases__ == (Known, Base)
assert UnknownFirst.__bases__ == (Base, Known)
assert TwoUnknown.__bases__ == (Base, SecondBase)
assert KnownFirst(3).y == 3
assert UnknownFirst(x=5).y == 5
assert KnownFirst().marker() == "known"
assert UnknownFirst().marker() == "base"
assert TwoUnknown(7).y == 7
assert TwoUnknown().other() == "second"
def test_generic_bases() -> None:
generic: Any = globals()["Generic"]
type_var: Any = globals()["T"]
assert GenericLast.__bases__ == (Base, generic)
assert GenericFirst.__bases__ == (generic, Base)
assert GenericLast.__orig_bases__ == (Base, generic[type_var])
assert GenericFirst.__orig_bases__ == (generic[type_var], Base)
assert GenericLast.__parameters__ == (type_var,)
assert GenericFirst.__parameters__ == (type_var,)
assert GenericLast[int](3).y == 3
assert GenericFirst[str](5).y == 5
assert KnownGenericBase.__bases__ == (Base, KnownGeneric)
assert KnownGenericBase.__orig_bases__ == (Base, KnownGeneric[int])
assert KnownGenericBase(7).y == 7
[file unchecked.py]
StrBase = str
class SecondBase:
def other(self):
return "second"
class Meta(type):
pass
class MetaBase(metaclass=Meta):
pass
class Base:
label = "base attribute"
def __init__(self, x=2):
if x < 0:
raise ValueError("negative")
self.y = x
def marker(self):
return "base"
[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 testClassVarDoesNotShadowMethodGlobal]
from typing import ClassVar
from testutil import assertRaises
class E(Exception):
pass
class C:
E: ClassVar[type[E]] = E
def m(self, b: bytes) -> None:
if not b:
raise E()
def test_class_var_does_not_shadow_method_global() -> None:
with assertRaises(E):
C().m(b"")
[case testInitSubclassWithClassVar]
from typing import ClassVar
class Base:
name: ClassVar[str] = "base"
required: ClassVar[int] = -1
def __init_subclass__(cls, **kwargs: object) -> None:
cls.required = len(cls.name)
class Child(Base):
name: ClassVar[str] = "child"
class GrandChild(Child):
name: ClassVar[str] = "grandchild"
class NoOverride(Base):
pass
[file driver.py]
from native import Base, Child, GrandChild, NoOverride
# __init_subclass__ should see the subclass's own ClassVar values
assert Child.name == "child"
assert Child.required == 5, f"expected 5, got {Child.required}"
assert GrandChild.name == "grandchild"
assert GrandChild.required == 10, f"expected 10, got {GrandChild.required}"
# No override should use inherited value
assert NoOverride.name == "base"
assert NoOverride.required == 4, f"expected 4, got {NoOverride.required}"
[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 # type: ignore
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 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)
def test_override() -> None:
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)
def test_override() -> None:
# 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)
def test_override() -> None:
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)
def test_override() -> None:
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)
def test_override() -> None:
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)
def test_override() -> None:
# 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 testNativeClassHiddenMetaclass]
# The stub hides the metaclass of the base class, so it's only detected at runtime
from hidden import Base
class C(Base):
pass
[file hidden.pyi]
class Base: ...
[file hidden.py]
class Meta(type):
pass
class Base(metaclass=Meta):
pass
[file driver.py]
try:
import native
except TypeError as e:
assert str(e) == "mypyc classes can't have a metaclass"
else:
assert False
[case testMetaclass]
from meta import Meta
class Nothing(metaclass=Meta):
pass
def ident(x): return x
@ident
class Test:
pass
[file meta.py]
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 Nothing
assert Nothing.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 testPickleFrozenDataclass]
import copy
import pickle
from dataclasses import dataclass
from typing import Any, Dict, List
from mypy_extensions import mypyc_attr
@dataclass(frozen=True)
class Config:
py_version: str = "3"
@dataclass(frozen=True)
class Frozen:
x: int
y: str
@dataclass(frozen=True)
class FrozenSub(Frozen):
z: List[int]
@dataclass(frozen=True, slots=True)
class FrozenSlots:
a: int
b: Dict[int, int]
setattr_calls: List[str] = []
@mypyc_attr(serializable=True)
class WithSetattr:
def __init__(self, x: int) -> None:
self.x = x
def __setattr__(self, name: str, value: object) -> None:
setattr_calls.append(name)
super().__setattr__(name, value)
def round_trips(obj: Any) -> List[Any]:
# Protocols 0 and 1 can't rebuild any native class
out = [pickle.loads(pickle.dumps(obj, protocol))
for protocol in range(2, pickle.HIGHEST_PROTOCOL + 1)]
return out + [copy.copy(obj), copy.deepcopy(obj)]
def test_frozen_dataclass() -> None:
for obj in Config(), Frozen(1, 'a'), FrozenSub(2, 'b', [3]), FrozenSlots(4, {5: 6}):
for copied in round_trips(obj):
assert copied == obj
assert copied is not obj
assert type(copied) is type(obj)
def test_copy_is_still_frozen() -> None:
f: Any = copy.copy(Frozen(1, 'a'))
try:
f.x = 2
except AttributeError as e:
assert type(e).__name__ == 'FrozenInstanceError'
else:
assert False
assert f.x == 1
def test_setattr_not_called() -> None:
# Python restores the state without calling __setattr__ too, by updating __dict__
obj = WithSetattr(1)
calls = len(setattr_calls)
for copied in round_trips(obj):
assert copied.x == 1
assert len(setattr_calls) == calls
[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
[out version>=3.11]
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 testPropertyReturningCallable]
from typing import Any, Callable
from mypy_extensions import trait
class Config:
def __init__(self) -> None:
self._sorting_function: Callable[..., list[str]] | None = None
@property
def sorting_function(self) -> Callable[..., list[str]]:
if self._sorting_function is not None:
return self._sorting_function
self._sorting_function = sorted
return self._sorting_function
def sort(self, items: list[str]) -> list[str]:
return self.sorting_function(items)
class Item:
def __init__(self, n: int) -> None:
self.n = n
class C:
@property
def no_args(self) -> Callable[[], str]:
return lambda: "called"
@property
def any_value(self) -> Any:
return lambda x: x * 2
@property
def item_type(self) -> type[Item]:
return Item
class Base:
@property
def f(self) -> Callable[[int], int]:
return lambda x: x + 1
class Derived(Base):
def call_super(self, x: int) -> int:
return super().f(x)
class AttrOverride(Base):
# A builtin, so that it isn't bound as a method when interpreted
f: Callable[[int], int] = abs
@trait
class T:
@property
def f(self) -> Callable[[int], int]:
return lambda x: x - 1
class UsesTrait(T):
pass
def call_f(b: Base, x: int) -> int:
return b.f(x)
def call_trait_f(t: T, x: int) -> int:
return t.f(x)
def test_property_returning_callable() -> None:
config = Config()
assert config.sorting_function(["b2", "b10", "b1"]) == ["b1", "b10", "b2"]
assert config.sorting_function(["a", "b"], reverse=True) == ["b", "a"]
assert config.sort(["b", "a"]) == ["a", "b"]
def test_property_returning_other_callables() -> None:
c = C()
assert c.no_args() == "called"
assert c.any_value(21) == 42
assert c.item_type(5).n == 5
def test_property_returning_callable_inherited() -> None:
assert call_f(Base(), 1) == 2
assert call_f(Derived(), 1) == 2
assert Derived().call_super(2) == 3
assert call_f(AttrOverride(), -5) == 5
assert AttrOverride().f(-6) == 6
assert call_trait_f(UsesTrait(), 1) == 0
[case testPropertyWithOtherDecorator]
from functools import cache, cached_property
from typing import Any, Callable
from mypy_extensions import mypyc_attr, trait
from testutil import assertRaises
def double(f: Callable[[Any], int]) -> Callable[[Any], int]:
def wrapper(self: Any) -> int:
return f(self) * 2
return wrapper
def to_str(f: Callable[[Any], int]) -> Callable[[Any], str]:
def wrapper(self: Any) -> str:
return f"<{f(self)}>"
return wrapper
class C:
def __init__(self) -> None:
self.calls = 0
@property
@double
def p(self) -> int:
self.calls += 1
return 21
@property
@to_str
def s(self) -> int:
return 5
@property
@to_str
@double
def two(self) -> int:
return 3
@property
@cache
def cached(self) -> int:
self.calls += 1
return 7
@cached_property
@double
def cp(self) -> int:
return 4
def via_self(self) -> int:
return self.p + 1
class Base:
@property
@double
def x(self) -> int:
return 1
class NativeOverride(Base):
@property
def x(self) -> int:
return 5
class DecoratedOverride(Base):
@property
@double
def x(self) -> int:
return 10
class AttrOverride(Base):
x: int = 7
@trait
class T:
@property
@double
def t(self) -> int:
return 4
class UsesTrait(T):
pass
@mypyc_attr(allow_interpreted_subclasses=True)
class Open:
@property
@double
def o(self) -> int:
return 8
@mypyc_attr(native_class=False)
class NonNative:
def __init__(self) -> None:
self.calls = 0
@property
@double
def p(self) -> int:
self.calls += 1
return 3
@property
@to_str
def s(self) -> int:
return 5
@property
@cache
def cached(self) -> int:
self.calls += 1
return 7
def get_x(b: Base) -> int:
return b.x
def get_t(t: T) -> int:
return t.t
def get_o(o: Open) -> int:
return o.o
def test_property_with_other_decorator() -> None:
c = C()
assert c.p == 42
assert c.p + 1 == 43
assert c.calls == 2
assert c.via_self() == 43
assert c.s == "<5>"
assert c.two == "<6>"
a: Any = c
assert a.p == 42
assert a.s == "<5>"
assert isinstance(getattr(C, "p"), property)
with assertRaises(AttributeError):
a.p = 1
def test_property_with_caching_decorator() -> None:
c = C()
assert c.cached == 7
assert c.cached == 7
assert c.calls == 1
assert C().cached == 7
assert c.cp == 8
def test_property_with_other_decorator_inherited() -> None:
assert get_x(Base()) == 2
assert get_x(NativeOverride()) == 5
assert NativeOverride().x == 5
assert get_x(DecoratedOverride()) == 20
assert DecoratedOverride().x == 20
assert get_x(AttrOverride()) == 7
assert AttrOverride().x == 7
assert get_t(UsesTrait()) == 8
assert UsesTrait().t == 8
def test_property_with_other_decorator_interpreted_subclass() -> None:
assert get_o(Open()) == 16
make_type: Any = type
sub = make_type("Sub", (Open,), {"o": 3})
assert get_o(sub()) == 3
def test_property_with_other_decorator_non_native() -> None:
n = NonNative()
assert n.p == 6
assert n.calls == 1
assert n.s == "<5>"
assert n.cached == 7
assert n.cached == 7
assert n.calls == 2
a: Any = n
assert a.p == 6
assert isinstance(getattr(NonNative, "p"), property)
with assertRaises(AttributeError):
a.p = 1
[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
import gc
from native import A, B, C, D, E, F, Foo, G
def test_leaks() -> None:
a = A()
gc.collect()
before = gc.get_objects()
for _ in range(100):
a.foo = Foo()
gc.collect()
after = gc.get_objects()
diff = len(after) - len(before)
# Small difference is expected, if the property setter call was leaking objects
# we would get a difference similar to the number of iterations above.
assert diff <= 2, diff
def set_foo(foo: Foo) -> None:
a = A()
a.foo = foo
def test_use_after_passing_to_prop_setter() -> None:
foo = Foo()
foo.attr = "before"
assert foo.attr == "before", foo.attr
set_foo(foo)
assert foo.attr == "before", foo.attr
foo.attr = "after"
assert foo.attr == "after", foo.attr
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
test_leaks()
test_use_after_passing_to_prop_setter()
[file driver.py]
# Run the tests in both interpreted and compiled mode
import other
import other_interpreted
[out]
[case testPropertySetterWiderThanGetter]
from typing import Union
from mypy_extensions import trait
class Wrapped:
def __init__(self, s: str) -> None:
self.s = s
@trait
class T:
@property
def value(self) -> Wrapped:
raise NotImplementedError
@value.setter
def value(self, v: Union[Wrapped, str]) -> None:
raise NotImplementedError
class C(T):
def __init__(self) -> None:
self._value = Wrapped("init")
self._n = 0
@property
def value(self) -> Wrapped:
return self._value
@value.setter
def value(self, v: Union[Wrapped, str]) -> None:
self._value = Wrapped(v) if isinstance(v, str) else v
@property
def n(self) -> int:
return self._n
@n.setter
def n(self, v: Union[int, str]) -> None:
self._n = int(v)
def set_via_class(c: C, v: Union[Wrapped, str]) -> str:
c.value = v
return c.value.s
def set_via_trait(t: T, v: Union[Wrapped, str]) -> str:
t.value = v
return t.value.s
def set_unboxed(c: C, v: Union[int, str]) -> int:
c.n = v
return c.n
def test_setter_wider_than_getter() -> None:
c = C()
assert set_via_class(c, "a") == "a"
assert set_via_class(c, Wrapped("b")) == "b"
assert set_via_trait(c, "c") == "c"
assert set_via_trait(c, Wrapped("d")) == "d"
assert set_unboxed(c, "5") == 5
assert set_unboxed(c, 6) == 6
[file driver.py]
from native import C, test_setter_wider_than_getter
test_setter_wider_than_getter()
c = C()
c.value = "py"
assert c.value.s == "py"
c.n = "7"
assert c.n == 7
[case testDecoratedMethodOverridesNativeMethod]
import functools
from typing import Any, Callable, TypeVar
from mypy_extensions import i64, mypyc_attr, trait
F = TypeVar("F", bound=Callable[..., Any])
def add_ten(f: F) -> F:
@functools.wraps(f)
def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any:
return f(self, *args, **kwargs) + 10
return wrapper # type: ignore[return-value]
def tag(f: F) -> F:
def wrapper(self: Any) -> str:
return "<" + f(self) + ">"
return wrapper # type: ignore[return-value]
class Base:
def method(self, x: int) -> int:
return x
def call(self, x: int) -> int:
return self.method(x)
class Derived(Base):
@add_ten
def method(self, x: int) -> int:
return super().method(x) * 2
class Sub(Derived):
def method(self, x: int) -> int:
return super().method(x) + 100
class ExplicitSub(Derived):
def method(self, x: int) -> int:
return Derived.method(self, x) + 100
def identity(f: Any) -> Any:
return f
class IdentityDerived(Base):
@identity
def method(self, x: int) -> int:
return 2
class IdentitySub(IdentityDerived):
def method(self, x: int) -> int:
return IdentityDerived.method(self, x)
class Derived2(Derived):
@add_ten
def method(self, x: int) -> int:
return super().method(x) * 2
class Sub2(Derived2):
def method(self, x: int) -> int:
return super().method(x) + Derived.method(self, x) + Derived2.method(self, x)
class WiderDerived2(Derived):
# Decorated override of a decorated override with a different signature
@add_ten
def method(self, x: object) -> int:
return 1000
class DefaultsBase:
def m(self, a: i64 = 1, b: float = 1.0, *, c: i64 = 1) -> float:
return 0.0
class Defaults(DefaultsBase):
@add_ten
def m(self, a: i64 = 2, b: float = 3.0, *, c: i64 = 4) -> float:
return float(a * 100) + b * 10 + float(c)
class ObjBase:
def m(self) -> object:
return 0
class NarrowerReturn(ObjBase):
@add_ten
def m(self) -> int:
return 1
@trait
class T:
def m(self) -> int:
return 0
class TraitImpl(T):
@add_ten
def m(self) -> int:
return 2
class TraitImplSub(TraitImpl):
def m(self) -> int:
return TraitImpl.m(self) + super().m()
class Repr:
@tag
def __repr__(self) -> str:
return "Repr"
def to_object_arg(f: Callable[[Any, int], int]) -> Callable[[Any, object], int]:
def wrapper(self: Any, x: object) -> int:
return f(self, int(str(x)))
return wrapper
def to_star_args(f: Callable[[Any, int, int], int]) -> Callable[..., int]:
def wrapper(self: Any, *args: int) -> int:
return f(self, len(args), 0)
return wrapper
def as_static(f: Any) -> Any:
wrap: Any = staticmethod
return wrap(lambda *args: len(args))
def as_class(f: Any) -> Any:
wrap: Any = classmethod
return wrap(lambda cls, *args: cls.__name__)
class SigBase:
def f(self, x: object) -> int:
return 0
def g(self) -> int:
return 0
def s(self) -> Any:
return None
def c(self) -> Any:
return None
class ChangesSig(SigBase):
# Calls typed against ChangesSig must use the decorated signatures
@to_object_arg
def f(self, x: int) -> int:
return x + 1
@to_star_args
def g(self, x: int, y: int) -> int:
return x + y
@as_static
def s(self) -> Any:
return None
@as_class
def c(self) -> Any:
return None
class ChangesSigSub(ChangesSig):
pass
@mypyc_attr(allow_interpreted_subclasses=True)
class OpenBase:
def m(self) -> int:
return 0
def n(self, a: i64 = 1, b: float = 1.0) -> float:
return 0.0
@mypyc_attr(allow_interpreted_subclasses=True)
class Open(OpenBase):
@add_ten
def m(self) -> int:
return 3
def via_base(b: Base, x: int) -> int:
return b.method(x)
def via_derived(d: Derived, x: int) -> int:
return d.method(x)
def via_obj_base(b: ObjBase) -> object:
return b.m()
def via_trait(t: T) -> int:
return t.m()
def via_open_base(b: OpenBase) -> int:
return b.m()
def via_open_base_defaults(b: OpenBase) -> list[float]:
return [b.n(), b.n(5), b.n(b=6.0)]
def via_sig_base(b: SigBase) -> list[Any]:
return [b.f("5"), b.g(), b.s(), b.c()]
def via_changes_sig(d: ChangesSig) -> list[Any]:
return [d.f("5"), d.g(), d.g(1, 2), d.s(), d.c(), ChangesSig.f(d, "6")]
def via_defaults_base(b: DefaultsBase) -> list[float]:
return [b.m(), b.m(5), b.m(b=6.0), b.m(c=7), b.m(5, 6.0, c=7)]
def test_decorated_override() -> None:
d = Derived()
assert via_base(d, 1) == 12
assert via_derived(d, 1) == 12
assert d.call(1) == 12
assert via_base(Base(), 1) == 1
assert via_base(Sub(), 1) == 112
assert via_base(ExplicitSub(), 1) == 112
assert Derived.method(ExplicitSub(), 1) == 12
assert via_base(IdentitySub(), 1) == 2
assert via_base(Derived2(), 1) == 34
assert via_base(Sub2(), 1) == 34 + 12 + 34
assert via_base(WiderDerived2(), 1) == 1010
assert via_derived(WiderDerived2(), 1) == 1010
assert via_defaults_base(Defaults()) == [244.0, 544.0, 274.0, 247.0, 577.0]
assert via_sig_base(ChangesSig()) == [6, 0, 0, "ChangesSig"]
assert via_sig_base(ChangesSigSub()) == [6, 0, 0, "ChangesSigSub"]
assert via_changes_sig(ChangesSig()) == [6, 0, 2, 0, "ChangesSig", 7]
assert via_obj_base(NarrowerReturn()) == 11
assert via_trait(TraitImpl()) == 12
assert via_trait(TraitImplSub()) == 24
assert repr(Repr()) == "<Repr>"
[typing fixtures/typing-full.pyi]
[file driver.py]
import inspect
from native import (
Derived, Open, OpenBase, via_open_base, via_open_base_defaults, test_decorated_override
)
test_decorated_override()
assert Derived().method(1) == 12
assert Derived.method.__name__ == "method"
assert list(inspect.signature(Derived.__dict__["method"].__wrapped__).parameters) == ["self", "x"]
class Inherits(Open):
pass
assert via_open_base(Inherits()) == 13
class InterpretedDefaults(OpenBase):
def n(self, a: int = 2, b: float = 3.0) -> float:
return a * 10 + b
assert via_open_base_defaults(InterpretedDefaults()) == [23.0, 53.0, 26.0]
[case testAttributeOverridesProperty]
from typing import Any
from mypy_extensions import trait
@trait
class T1:
@property
def x(self) -> int: ...
@property
def y(self) -> int: ...
class C1(T1):
x: int = 1
y: int = 4
def test_read_only_property_in_trait_implemented_as_attribute() -> None:
c = C1()
c.x = 5
assert c.x == 5
assert c.y == 4
c.y = 6
assert c.y == 6
t: T1 = C1()
assert t.y == 4
t = c
assert t.x == 5
assert t.y == 6
a: Any = c
assert a.x == 5
assert a.y == 6
a.x = 7
a.y = 8
assert a.x == 7
assert a.y == 8
class B2:
@property
def x(self) -> int:
return 11
@property
def y(self) -> int:
return 25
class C2(B2):
x: int = 1
y: int = 4
def test_read_only_property_in_class_implemented_as_attribute() -> None:
c = C2()
c.x = 5
assert c.x == 5
assert c.y == 4
c.y = 6
assert c.y == 6
b: B2 = C2()
assert b.y == 4
b = c
assert b.x == 5
assert b.y == 6
a: Any = c
assert a.x == 5
assert a.y == 6
a.x = 7
a.y = 8
assert a.x == 7
assert a.y == 8
@trait
class T3:
@property
def x(self) -> int: ...
@property
def y(self) -> int: ...
class B3:
x: int = 1
y: int = 4
class C3(B3, T3):
pass
def test_read_only_property_implemented_as_attribute_indirectly() -> None:
c = C3()
c.x = 5
assert c.x == 5
assert c.y == 4
c.y = 6
assert c.y == 6
t: T3 = C3()
assert t.y == 4
t = c
assert t.x == 5
assert t.y == 6
a: Any = c
assert a.x == 5
assert a.y == 6
a.x = 7
a.y = 8
assert a.x == 7
assert a.y == 8
@trait
class T4:
@property
def x(self) -> int: ...
@x.setter
def x(self, v1: int) -> None: ...
@property
def y(self) -> int: ...
@y.setter
def y(self, v2: int) -> None: ...
class C4(T4):
x: int = 1
y: int = 4
def test_read_write_property_implemented_as_attribute() -> None:
c = C4()
c.x = 5
assert c.x == 5
assert c.y == 4
c.y = 6
assert c.y == 6
t: T4 = C4()
assert t.y == 4
t.x = 5
assert t.x == 5
t.y = 6
assert t.y == 6
a: Any = c
assert a.x == 5
assert a.y == 6
a.x = 7
a.y = 8
assert a.x == 7
assert a.y == 8
@trait
class T5:
@property
def x(self) -> int: ...
@x.setter
def x(self, v1: int) -> None: ...
@property
def y(self) -> int: ...
@y.setter
def y(self, v2: int) -> None: ...
class B5:
x: int = 1
y: int = 4
class BB5(B5):
pass
class C5(BB5, T5):
pass
def test_read_write_property_indirectly_implemented_as_attribute() -> None:
c = C5()
c.x = 5
assert c.x == 5
assert c.y == 4
c.y = 6
assert c.y == 6
t: T5 = C5()
assert t.y == 4
t.x = 5
assert t.x == 5
t.y = 6
assert t.y == 6
a: Any = c
assert a.x == 5
assert a.y == 6
a.x = 7
a.y = 8
assert a.x == 7
assert a.y == 8
[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): # type: ignore[misc, valid-type]
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 DerivedInterpreted2(b): # type: ignore[misc, valid-type]
def __init__(self):
super().__init__('y')
d2 = DerivedInterpreted2()
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
[case testClassWithInherited__call__]
class Base:
def __call__(self) -> int:
return 1
class Derived(Base):
pass
def test_inherited() -> None:
assert Derived()() == 1
[case testClassWithFinalAttribute]
from typing import Final
class C:
A: Final = -1
a: Final = [A]
def test_final_attribute() -> None:
assert C.A == -1
assert C.a == [-1]
def test_final_attribute_through_instance() -> None:
assert C().A == -1
assert C().a == [-1]
def test_final_attribute_not_per_instance() -> None:
# A class-body Final has a single value shared by every instance, so a
# mutable one must not be rebuilt per instance.
assert C().a is C.a
assert C().a is C().a
[case testClassWithFinalDecorator]
from typing import final
@final
class C:
def a(self) -> int:
return 1
def test_class_final_attribute() -> None:
assert C().a() == 1
[case testClassWithFinalDecoratorCtor]
from typing import final
@final
class C:
def __init__(self) -> None:
self.a = 1
def b(self) -> int:
return 2
@property
def c(self) -> int:
return 3
def test_class_final_attribute() -> None:
assert C().a == 1
assert C().b() == 2
assert C().c == 3
[case testClassWithFinalDecoratorInheritedWithProperties]
from typing import final
class B:
def a(self) -> int:
return 2
@property
def b(self) -> int:
return self.a() + 2
@property
def c(self) -> int:
return 3
def test_class_final_attribute_basic() -> None:
assert B().a() == 2
assert B().b == 4
assert B().c == 3
@final
class C(B):
def a(self) -> int:
return 1
@property
def b(self) -> int:
return self.a() + 1
def fn(cl: B) -> int:
return cl.a()
def test_class_final_attribute_inherited() -> None:
assert C().a() == 1
assert fn(C()) == 1
assert B().a() == 2
assert fn(B()) == 2
assert B().b == 4
assert C().b == 2
assert B().c == 3
assert C().c == 3
[case testClassWithFinalAttributeAccess]
from typing import Final
class C:
a: Final = {'x': 'y'}
b: Final = C.a
def test_final_attribute() -> None:
assert C.a['x'] == 'y'
assert C.b['x'] == 'y'
assert C.a is C.b
[case testFinalInstanceAttributeCannotBeRebound]
from typing import Any, Final
from testutil import assertRaises
class C:
def __init__(self, x: int) -> None:
self.x: Final[int] = x
class D(C):
def __init__(self, x: int, y: int) -> None:
super().__init__(x)
self.y: Final[int] = y
def rebind_via_any(o: Any, value: int) -> None:
o.x = value
def test_rebind_via_any() -> None:
c = C(1)
with assertRaises(AttributeError):
rebind_via_any(c, 2)
assert c.x == 1
def test_rebind_via_setattr() -> None:
c = C(1)
with assertRaises(AttributeError):
setattr(c, "x", 3)
assert c.x == 1
def test_rebind_inherited_via_setattr() -> None:
d = D(1, 2)
# Inherited Final attribute can't be modified.
with assertRaises(AttributeError):
setattr(d, "x", 3)
# The subclass's own Final attribute can't be modified either.
with assertRaises(AttributeError):
setattr(d, "y", 4)
assert d.x == 1
assert d.y == 2
[case testFinalRefcountedAttributeRead]
# Exercises reading Final reference-counted ('PyObject *') attributes, which on
# free-threaded builds use a faster read path (CPy_GetAttrRefFinal) that skips the
# concurrent-writer guard, since a Final attribute is never rebound.
from typing import Final
class C:
def __init__(self, s: str, items: list[int]) -> None:
self.s: Final = s
self.items: Final = items
def read_s(self) -> str:
return self.s
def read_items(self) -> list[int]:
return self.items
def chained_len(self) -> int:
# borrowed intermediate read of a Final attr, then a call on it
return len(self.items)
class D(C):
def __init__(self, s: str, items: list[int], t: str) -> None:
super().__init__(s, items)
self.t: Final = t
def read_inherited(self) -> str:
return self.s
def test_read_final_object_attrs() -> None:
c = C("hello", [1, 2, 3])
# Read via getter (property access) and via native method bodies.
assert c.s == "hello"
assert c.read_s() == "hello"
assert c.items == [1, 2, 3]
assert c.read_items() == [1, 2, 3]
assert c.chained_len() == 3
# Reading repeatedly must not corrupt the refcount / free the value.
for _ in range(1000):
assert c.read_s() == "hello"
assert c.read_items() == [1, 2, 3]
assert c.s == "hello"
assert c.read_items() is c.items
def test_read_inherited_final_attr() -> None:
d = D("a", [4, 5], "b")
assert d.s == "a"
assert d.t == "b"
assert d.read_s() == "a"
assert d.read_inherited() == "a"
assert d.read_items() == [4, 5]
[case testClassDerivedFromIntEnum]
from enum import IntEnum, auto
class Player(IntEnum):
MIN = auto()
print(f'{Player.MIN = }')
[file driver.py]
from native import Player
[out]
Player.MIN = <Player.MIN: 1>
[case testBufferRoundTrip_librt_internal]
from __future__ import annotations
from typing import Final, Any
from mypy_extensions import u8
from librt.internal import (
ReadBuffer, WriteBuffer, write_bool, read_bool, write_str, read_str, write_float, read_float,
write_int, read_int, write_tag, read_tag, write_bytes, read_bytes,
cache_version, extract_symbol,
)
from testutil import assertRaises
Tag = u8
TAG_A: Final[Tag] = 33
TAG_B: Final[Tag] = 255
TAG_SPECIAL: Final[Tag] = 239
def test_buffer_basic() -> None:
assert cache_version() == 0
w = WriteBuffer()
write_str(w, "foo")
r = ReadBuffer(w.getvalue())
assert read_str(r) == "foo"
def test_buffer_grow() -> None:
w = WriteBuffer()
n = 100 * 1000
for i in range(n):
write_int(w, i & 63)
r = ReadBuffer(w.getvalue())
for i in range(n):
assert read_int(r) == (i & 63)
with assertRaises(ValueError):
read_int(r)
def test_buffer_primitive_types() -> None:
a1: Any = WriteBuffer()
w: WriteBuffer = a1
write_str(w, "foo")
data = w.getvalue()
assert read_str(ReadBuffer(data)) == "foo"
a2: Any = ReadBuffer(b"foo")
with assertRaises(TypeError):
w2: WriteBuffer = a2
a3: Any = ReadBuffer(data)
r: ReadBuffer = a3
assert read_str(r) == "foo"
a4: Any = WriteBuffer()
with assertRaises(TypeError):
r2: ReadBuffer = a4
def test_type_check_args_in_write_functions() -> None:
# Test calling wrapper functions with invalid arg types
from librt import internal
alias: Any = internal
w = WriteBuffer()
with assertRaises(TypeError):
alias.write_str(None, "foo")
with assertRaises(TypeError):
alias.write_str(w, None)
with assertRaises(TypeError):
alias.write_bool(None, True)
with assertRaises(TypeError):
alias.write_bool(w, None)
with assertRaises(TypeError):
alias.write_bytes(None, b"foo")
with assertRaises(TypeError):
alias.write_bytes(w, None)
with assertRaises(TypeError):
alias.write_float(None, 1.5)
with assertRaises(TypeError):
alias.write_float(w, None)
with assertRaises(TypeError):
alias.write_int(None, 15)
with assertRaises(TypeError):
alias.write_int(w, None)
with assertRaises(TypeError):
alias.write_tag(None, 15)
with assertRaises(TypeError):
alias.write_tag(w, None)
def test_type_check_buffer_in_read_functions() -> None:
# Test calling wrapper functions with invalid arg types
from librt import internal
alias: Any = internal
with assertRaises(TypeError):
alias.read_str(None)
with assertRaises(TypeError):
alias.read_bool(None)
with assertRaises(TypeError):
alias.read_bytes(None)
with assertRaises(TypeError):
alias.read_float(None)
with assertRaises(TypeError):
alias.read_int(None)
with assertRaises(TypeError):
alias.read_tag(None)
def test_buffer_roundtrip() -> None:
b: WriteBuffer | ReadBuffer
b = WriteBuffer()
write_str(b, "foo")
write_bool(b, True)
write_str(b, "bar" * 1000)
write_bool(b, False)
write_bytes(b, b"bar")
write_bytes(b, b"bar" * 100)
write_bytes(b, b"")
write_bytes(b, b"a" * 117)
write_bytes(b, b"a" * 118)
write_float(b, 0.1)
write_float(b, -1.0)
write_float(b, -113.0)
write_int(b, 0)
write_int(b, 1)
write_tag(b, TAG_A)
write_tag(b, TAG_SPECIAL)
write_tag(b, TAG_B)
write_int(b, 2)
write_int(b, 2 ** 85)
write_int(b, 255)
write_int(b, -1)
write_int(b, -255)
write_int(b, 536860911)
write_int(b, 536860912)
write_int(b, 1234567891)
b = ReadBuffer(b.getvalue())
assert read_str(b) == "foo"
assert read_bool(b) is True
assert read_str(b) == "bar" * 1000
assert read_bool(b) is False
assert read_bytes(b) == b"bar"
assert read_bytes(b) == b"bar" * 100
assert read_bytes(b) == b""
assert read_bytes(b) == b"a" * 117
assert read_bytes(b) == b"a" * 118
assert read_float(b) == 0.1
assert read_float(b) == -1.0
assert read_float(b) == -113.0
assert read_int(b) == 0
assert read_int(b) == 1
assert read_tag(b) == TAG_A
assert read_tag(b) == TAG_SPECIAL
assert read_tag(b) == TAG_B
assert read_int(b) == 2
assert read_int(b) == 2 ** 85
assert read_int(b) == 255
assert read_int(b) == -1
assert read_int(b) == -255
assert read_int(b) == 536860911
assert read_int(b) == 536860912
assert read_int(b) == 1234567891
def test_buffer_int_size() -> None:
b: WriteBuffer | ReadBuffer
for i in (-10, -9, 0, 116, 117):
b = WriteBuffer()
write_int(b, i)
assert len(b.getvalue()) == 1
b = ReadBuffer(b.getvalue())
assert read_int(b) == i
for i in (-100, -11, 118, 12344, 16283):
b = WriteBuffer()
write_int(b, i)
assert len(b.getvalue()) == 2
b = ReadBuffer(b.getvalue())
assert read_int(b) == i
for i in (-10000, 16284, 123456789):
b = WriteBuffer()
write_int(b, i)
assert len(b.getvalue()) == 4
b = ReadBuffer(b.getvalue())
assert read_int(b) == i
def test_buffer_int_powers() -> None:
# 0, 1, 2 are tested above
for p in range(2, 200):
b = WriteBuffer()
write_int(b, 1 << p)
write_int(b, (1 << p) - 1)
write_int(b, -1 << p)
write_int(b, (-1 << p) + 1)
rb = ReadBuffer(b.getvalue())
assert read_int(rb) == 1 << p
assert read_int(rb) == (1 << p) - 1
assert read_int(rb) == -1 << p
assert read_int(rb) == (-1 << p) + 1
def test_positive_long_int_serialized_bytes() -> None:
b = WriteBuffer()
n = 0x123456789ab
write_int(b, n)
x = b.getvalue()
# Two prefix bytes, followed by little endian encoded integer in variable-length format
assert x == b"\x0f\x2c\xab\x89\x67\x45\x23\x01"
rb = ReadBuffer(x)
assert read_int(rb) == n
def test_negative_long_int_serialized_bytes() -> None:
b = WriteBuffer()
n = -0x123456789abcde
write_int(b, n)
x = b.getvalue()
assert x == b"\x0f\x32\xde\xbc\x9a\x78\x56\x34\x12"
rb = ReadBuffer(x)
assert read_int(rb) == n
def test_buffer_str_size() -> None:
b: WriteBuffer | ReadBuffer
for s in ("", "a", "a" * 117):
b = WriteBuffer()
write_str(b, s)
assert len(b.getvalue()) == len(s) + 1
b = ReadBuffer(b.getvalue())
assert read_str(b) == s
for s in ("a" * 118, "a" * 16283):
b = WriteBuffer()
write_str(b, s)
assert len(b.getvalue()) == len(s) + 2
b = ReadBuffer(b.getvalue())
assert read_str(b) == s
def test_extract_symbol_sentinel_literal() -> None:
data = WriteBuffer()
write_tag(data, 8) # LITERAL_SENTINEL
write_str(data, "__main__.MISSING")
write_str(data, "MISSING")
write_tag(data, 255) # END_TAG
payload = data.getvalue()
assert extract_symbol(ReadBuffer(payload)) == payload
[file driver.py]
from native import *
test_buffer_basic()
test_buffer_grow()
test_buffer_primitive_types()
test_type_check_args_in_write_functions()
test_type_check_buffer_in_read_functions()
test_buffer_roundtrip()
test_buffer_int_size()
test_buffer_str_size()
test_buffer_int_powers()
test_positive_long_int_serialized_bytes()
test_negative_long_int_serialized_bytes()
test_extract_symbol_sentinel_literal()
def test_buffer_basic_interpreted() -> None:
b = WriteBuffer()
write_str(b, "foo")
b = ReadBuffer(b.getvalue())
assert read_str(b) == "foo"
def test_buffer_roundtrip_interpreted() -> None:
b = WriteBuffer()
write_str(b, "foo")
write_bool(b, True)
write_str(b, "bar" * 1000)
write_bool(b, False)
write_bytes(b, b"bar")
write_bytes(b, b"bar" * 100)
write_bytes(b, b"")
write_bytes(b, b"a" * 117)
write_bytes(b, b"a" * 118)
write_float(b, 0.1)
write_int(b, 0)
write_int(b, 1)
write_tag(b, 33)
write_tag(b, 239)
write_tag(b, 255)
write_int(b, 2)
write_int(b, 2 ** 85)
write_int(b, 255)
write_int(b, -1)
write_int(b, -255)
write_int(b, 536860911)
write_int(b, 536860912)
write_int(b, 1234567891)
b = ReadBuffer(b.getvalue())
assert read_str(b) == "foo"
assert read_bool(b) is True
assert read_str(b) == "bar" * 1000
assert read_bool(b) is False
assert read_bytes(b) == b"bar"
assert read_bytes(b) == b"bar" * 100
assert read_bytes(b) == b""
assert read_bytes(b) == b"a" * 117
assert read_bytes(b) == b"a" * 118
assert read_float(b) == 0.1
assert read_int(b) == 0
assert read_int(b) == 1
assert read_tag(b) == 33
assert read_tag(b) == 239
assert read_tag(b) == 255
assert read_int(b) == 2
assert read_int(b) == 2 ** 85
assert read_int(b) == 255
assert read_int(b) == -1
assert read_int(b) == -255
assert read_int(b) == 536860911
assert read_int(b) == 536860912
assert read_int(b) == 1234567891
def test_buffer_int_size_interpreted() -> None:
for i in (-10, -9, 0, 116, 117):
b = WriteBuffer()
write_int(b, i)
assert len(b.getvalue()) == 1
b = ReadBuffer(b.getvalue())
assert read_int(b) == i
for i in (-100, -11, 118, 12344, 16283):
b = WriteBuffer()
write_int(b, i)
assert len(b.getvalue()) == 2
b = ReadBuffer(b.getvalue())
assert read_int(b) == i
for i in (-10000, 16284, 123456789):
b = WriteBuffer()
write_int(b, i)
assert len(b.getvalue()) == 4
b = ReadBuffer(b.getvalue())
assert read_int(b) == i
def test_buffer_int_powers_interpreted() -> None:
# 0, 1, 2 are tested above
for p in range(2, 9):
b = WriteBuffer()
write_int(b, 1 << p)
write_int(b, -1 << p)
b = ReadBuffer(b.getvalue())
assert read_int(b) == 1 << p
assert read_int(b) == -1 << p
def test_buffer_str_size_interpreted() -> None:
for s in ("", "a", "a" * 117):
b = WriteBuffer()
write_str(b, s)
assert len(b.getvalue()) == len(s) + 1
b = ReadBuffer(b.getvalue())
assert read_str(b) == s
for s in ("a" * 118, "a" * 16283):
b = WriteBuffer()
write_str(b, s)
assert len(b.getvalue()) == len(s) + 2
b = ReadBuffer(b.getvalue())
assert read_str(b) == s
test_buffer_basic_interpreted()
test_buffer_roundtrip_interpreted()
test_buffer_int_size_interpreted()
test_buffer_str_size_interpreted()
test_buffer_int_powers_interpreted()
[case testBufferEmpty_librt_internal]
from librt.internal import WriteBuffer, ReadBuffer, write_int, read_int
def test_empty() -> None:
b = WriteBuffer()
write_int(b, 42)
b1 = ReadBuffer(b.getvalue())
assert read_int(b1) == 42
[case testEnumMethodCalls]
from enum import Enum
from typing import overload, Optional, Union
class C:
def foo(self, x: Test) -> bool:
assert Test.ONE.is_one()
assert x.next(2) == Test.THREE
assert x.prev(2) == Test.ONE
assert x.enigma(22)
assert x.enigma("22") == 22
return x.is_one(inverse=True)
class Test(Enum):
ONE = 1
TWO = 2
THREE = 3
def is_one(self, *, inverse: bool = False) -> bool:
if inverse:
return self != Test.ONE
return self == Test.ONE
@classmethod
def next(cls, val: int) -> Test:
return cls(val + 1)
@staticmethod
def prev(val: int) -> Test:
return Test(val - 1)
@overload
def enigma(self, val: int) -> bool: ...
@overload
def enigma(self, val: Optional[str] = None) -> int: ...
def enigma(self, val: Union[int, str, None] = None) -> Union[int, bool]:
if isinstance(val, int):
return self.is_one()
return 22
[file driver.py]
from native import Test, C
assert Test.ONE.is_one()
assert Test.TWO.is_one(inverse=True)
assert not C().foo(Test.ONE)
assert Test.next(2) == Test.THREE
assert Test.prev(2) == Test.ONE
assert Test.ONE.enigma(22)
assert Test.ONE.enigma("22") == 22
[case testStaticCallsWithUnpackingArgs]
from typing import Tuple
class Foo:
@staticmethod
def static(a: int, b: int, c: int) -> Tuple[int, int, int]:
return (c+1, a+2, b+3)
@classmethod
def clsmethod(cls, a: int, b: int, c: int) -> Tuple[int, int, int]:
return (c+1, a+2, b+3)
print(Foo.static(*[10, 20, 30]))
print(Foo.static(*(40, 50), *[60]))
assert Foo.static(70, 80, *[90]) == Foo.clsmethod(70, *(80, 90))
[file driver.py]
import native
[out]
(31, 12, 23)
(61, 42, 53)
[case testDataclassInitVar]
import dataclasses
@dataclasses.dataclass
class C:
init_v: dataclasses.InitVar[int]
v: float = dataclasses.field(init=False)
def __post_init__(self, init_v):
self.v = init_v + 0.1
[file driver.py]
import native
print(native.C(22).v)
[out]
22.1
[case testLastParentEnum]
from enum import Enum
class ColorCode(str, Enum):
OKGREEN = "okgreen"
[file driver.py]
import native
print(native.ColorCode.OKGREEN.value)
[out]
okgreen
[case testAttrWithSlots]
import attr
@attr.s(slots=True)
class A:
ints: list[int] = attr.ib()
[file driver.py]
import native
print(native.A(ints=[1, -17]).ints)
[out]
\[1, -17]
[case testDataclassClassReference]
from __future__ import annotations
from dataclasses import dataclass
class BackwardDefinedClass:
pass
@dataclass
class Data:
bitem: BackwardDefinedClass
bitems: 'BackwardDefinedClass'
fitem: ForwardDefinedClass
fitems: 'ForwardDefinedClass'
class ForwardDefinedClass:
pass
def test_function():
d = Data(
bitem=BackwardDefinedClass(),
bitems=BackwardDefinedClass(),
fitem=ForwardDefinedClass(),
fitems=ForwardDefinedClass(),
)
assert(isinstance(d.bitem, BackwardDefinedClass))
assert(isinstance(d.bitems, BackwardDefinedClass))
assert(isinstance(d.fitem, ForwardDefinedClass))
assert(isinstance(d.fitems, ForwardDefinedClass))
[case testDelForDictSubclass]
events: list[str] = []
class Item:
def __del__(self) -> None:
events.append("deleting Item")
class DictSubclass(dict):
def __del__(self) -> None:
events.append("deleting DictSubclass")
def test_dict_subclass_dealloc() -> None:
d = DictSubclass()
d["item"] = Item()
del d
[file driver.py]
from native import events, test_dict_subclass_dealloc
test_dict_subclass_dealloc()
assert events == ["deleting DictSubclass", "deleting Item"], events
[case testDel]
class A:
def __del__(self):
print("deleting A...")
class B:
def __del__(self):
print("deleting B...")
class C(B):
def __init__(self):
self.a = A()
def __del__(self):
print("deleting C...")
super().__del__()
class D(A):
pass
# Just make sure that this class compiles (see issue mypy#19175). testDelForDictSubclass tests for
# correct output.
class NormDict(dict):
def __del__(self) -> None:
pass
[file driver.py]
import native
native.C()
native.D()
[out]
deleting C...
deleting B...
deleting A...
deleting A...
[case testDelCircular]
import dataclasses
import typing
i: int = 1
@dataclasses.dataclass
class C:
var: typing.Optional["C"] = dataclasses.field(default=None)
def __del__(self):
global i
print(f"deleting C{i}...")
i = i + 1
[file driver.py]
import native
import gc
c1 = native.C()
c2 = native.C()
c1.var = c2
c2.var = c1
del c1
del c2
gc.collect()
[out]
deleting C1...
deleting C2...
[case testDelException]
# The error message in the expected output of this test does not match CPython's error message due to the way mypyc compiles Python classes. If the error message is fixed, the expected output of this test will also change.
class F:
def __del__(self):
if True:
raise Exception("e2")
[file driver.py]
import native
f = native.F()
del f
[out]
Exception ignored in: <native.F object at 0x000000000000>
Traceback (most recent call last):
File "native.py", line 5, in __del__
raise Exception("e2")
Exception: e2
[case testMypycAttrNativeClass]
from mypy_extensions import mypyc_attr
from testutil import assertRaises
@mypyc_attr(native_class=False)
class AnnontatedNonExtensionClass:
pass
class DerivedClass(AnnontatedNonExtensionClass):
pass
class ImplicitExtensionClass():
pass
@mypyc_attr(native_class=True)
class AnnotatedExtensionClass():
pass
def test_function():
setattr(AnnontatedNonExtensionClass, 'attr_class', 5)
assert(hasattr(AnnontatedNonExtensionClass, 'attr_class') == True)
assert(getattr(AnnontatedNonExtensionClass, 'attr_class') == 5)
delattr(AnnontatedNonExtensionClass, 'attr_class')
assert(hasattr(AnnontatedNonExtensionClass, 'attr_class') == False)
inst = AnnontatedNonExtensionClass()
setattr(inst, 'attr_instance', 6)
assert(hasattr(inst, 'attr_instance') == True)
assert(getattr(inst, 'attr_instance') == 6)
delattr(inst, 'attr_instance')
assert(hasattr(inst, 'attr_instance') == False)
setattr(DerivedClass, 'attr_class', 5)
assert(hasattr(DerivedClass, 'attr_class') == True)
assert(getattr(DerivedClass, 'attr_class') == 5)
delattr(DerivedClass, 'attr_class')
assert(hasattr(DerivedClass, 'attr_class') == False)
derived_inst = DerivedClass()
setattr(derived_inst, 'attr_instance', 6)
assert(hasattr(derived_inst, 'attr_instance') == True)
assert(getattr(derived_inst, 'attr_instance') == 6)
delattr(derived_inst, 'attr_instance')
assert(hasattr(derived_inst, 'attr_instance') == False)
ext_inst = ImplicitExtensionClass()
with assertRaises(AttributeError):
setattr(ext_inst, 'attr_instance', 6)
explicit_ext_inst = AnnotatedExtensionClass()
with assertRaises(AttributeError):
setattr(explicit_ext_inst, 'attr_instance', 6)
[case testMypycAttrNativeClassDunder]
from mypy_extensions import mypyc_attr
from typing import Generic, Optional, TypeVar
_T = TypeVar("_T")
get_count = set_count = del_count = 0
@mypyc_attr(native_class=False)
class Bar(Generic[_T]):
# Note the lack of __deletable__
def __init__(self) -> None:
self.value: str = 'start'
def __get__(self, instance: _T, owner: Optional[type[_T]] = None) -> str:
global get_count
get_count += 1
return self.value
def __set__(self, instance: _T, value: str) -> None:
global set_count
set_count += 1
self.value = value
def __delete__(self, instance: _T) -> None:
global del_count
del_count += 1
del self.value
@mypyc_attr(native_class=False)
class Foo(object):
bar: Bar = Bar()
[file driver.py]
import native
f = native.Foo()
assert(hasattr(f, 'bar'))
assert(native.get_count == 1)
assert(f.bar == 'start')
assert(native.get_count == 2)
f.bar = 'test'
assert(f.bar == 'test')
assert(native.set_count == 1)
del f.bar
assert(not hasattr(f, 'bar'))
assert(native.del_count == 1)
[case testMypycAttrNativeClassMeta]
from mypy_extensions import mypyc_attr
from typing import ClassVar, TypeVar
_T = TypeVar("_T")
@mypyc_attr(native_class=False)
class M(type):
count: ClassVar[int] = 0
def make(cls: type[_T]) -> _T:
M.count += 1
return cls()
# implicit native_class=False
# see testMypycAttrNativeClassMetaError for when trying to set it True
class A(metaclass=M):
pass
[file driver.py]
import native
a: native.A = native.A.make()
assert(native.A.count == 1)
class B(native.A):
pass
b: B = B.make()
assert(B.count == 2)
[case testTypeVarNarrowing]
from typing import TypeVar
class B:
def __init__(self, x: int) -> None:
self.x = x
class C(B):
def __init__(self, x: int, y: str) -> None:
self.x = x
self.y = y
T = TypeVar("T", bound=B)
def f(x: T) -> T:
if isinstance(x, C):
print("C", x.y)
return x
print("B", x.x)
return x
[file driver.py]
from native import f, B, C
f(B(1))
f(C(1, "yes"))
[out]
B 1
C yes
[case testTypeObjectName]
from typing import Any
import re
from dynamic import E, foo, Thing
class C: pass
class D(C): pass
def type_name(t: type[object]) -> str:
return t.__name__
def any_name(x: Any) -> str:
return x.__name__
def assert_type_name(x: Any) -> None:
assert type_name(x) == getattr(x, "__name__")
assert any_name(x) == getattr(x, "__name__")
def assert_any_name(x: Any) -> None:
assert any_name(x) == getattr(x, "__name__")
def test_type_name() -> None:
assert_type_name(C)
assert_type_name(D)
assert_type_name(int)
assert_type_name(E)
assert_type_name(re.Pattern)
def test_module_name() -> None:
assert_any_name(re)
def test_function_name() -> None:
assert_any_name(any_name)
assert_any_name(foo)
def test_obj_name() -> None:
assert_any_name(Thing())
[file dynamic.py]
class E: pass
def foo(): pass
class Thing:
def __init__(self):
self.__name__ = "xyz"
[case testTypeOfObject]
from typing import Any
from dynamic import Dyn
class Foo: pass
class Bar(Foo): pass
def generic_type(x) -> type[object]:
return x.__class__
def test_built_in_type() -> None:
i: Any = int
l: Any = list
assert type(i()) is i().__class__
assert type(i()) is int
assert type(l()) is list
n = 5
assert n.__class__ is i
def test_native_class() -> None:
f_any: Any = Foo()
b_any: Any = Bar()
f: Foo = f_any
b: Foo = b_any
if int("1"): # use int("1") to avoid constant folding
assert type(f) is Foo
assert type(b) is Bar
if int("2"):
assert f.__class__ is Foo
assert b.__class__ is Bar
if int("3"):
assert f_any.__class__ is Foo
assert b_any.__class__ is Bar
if int("4"):
assert type(f_any) is Foo
assert type(b_any) is Bar
def test_python_class() -> None:
d = Dyn()
assert type(d) is Dyn
assert d.__class__ is Dyn
[file dynamic.py]
class Dyn: pass
[case testDunderNew]
from __future__ import annotations
from typing import Any, Union
from testutil import assertRaises
class Add:
l: IntLike
r: IntLike
def __new__(cls, l: IntLike, r: IntLike) -> Any:
return (
l if r == 0 else
r if l == 0 else
super().__new__(cls)
)
def __init__(self, l: IntLike, r: IntLike):
self.l = l
self.r = r
IntLike = Union[int, Add]
class RaisesException:
def __new__(cls, val: int) -> RaisesException:
if val == 0:
raise RuntimeError("Invalid value!")
return super().__new__(cls)
def __init__(self, val: int) -> None:
self.val = val
class ClsArgNotPassed:
def __new__(cls) -> Any:
return super().__new__(str)
def test_dunder_new() -> None:
add_instance: Any = Add(1, 5)
assert type(add_instance) == Add
assert add_instance.l == 1
assert add_instance.r == 5
# TODO: explicit types should not be needed but mypy does not use
# the return type of __new__ which makes mypyc add casts to Add.
right_int: Any = Add(0, 5)
assert type(right_int) == int
assert right_int == 5
left_int: Any = Add(1, 0)
assert type(left_int) == int
assert left_int == 1
with assertRaises(RuntimeError, "Invalid value!"):
raised = RaisesException(0)
not_raised = RaisesException(1)
assert not_raised.val == 1
with assertRaises(TypeError, "object.__new__(str) is not safe, use str.__new__()"):
str_as_cls = ClsArgNotPassed()
[case testDunderNewInInterpreted]
from __future__ import annotations
from typing import Any, Union
class Add:
l: IntLike
r: IntLike
def __new__(cls, l: IntLike, r: IntLike) -> Any:
print(f'running __new__ with {l} and {r}')
return (
l if r == 0 else
r if l == 0 else
super().__new__(cls)
)
def __init__(self, l: IntLike, r: IntLike):
self.l = l
self.r = r
def __repr__(self) -> str:
return f'({self.l} + {self.r})'
IntLike = Union[int, Add]
class RaisesException:
def __new__(cls, val: int) -> RaisesException:
if val == 0:
raise RuntimeError("Invalid value!")
return super().__new__(cls)
def __init__(self, val: int) -> None:
self.val = val
class ClsArgNotPassed:
def __new__(cls) -> Any:
return super().__new__(str)
[file driver.py]
from native import Add, ClsArgNotPassed, RaisesException
from testutil import assertRaises
print(f'{Add(1, 5)=}')
print(f'{Add(0, 5)=}')
print(f'{Add(1, 0)=}')
with assertRaises(RuntimeError, "Invalid value!"):
raised = RaisesException(0)
not_raised = RaisesException(1)
assert not_raised.val == 1
with assertRaises(TypeError, "object.__new__(str) is not safe, use str.__new__()"):
str_as_cls = ClsArgNotPassed()
[out]
running __new__ with 1 and 5
Add(1, 5)=(1 + 5)
running __new__ with 0 and 5
Add(0, 5)=5
running __new__ with 1 and 0
Add(1, 0)=1
[case testObjectDunderNew]
from __future__ import annotations
from typing import Any, Union
from testutil import assertRaises
class Add:
l: IntLike
r: IntLike
def __new__(cls, l: IntLike, r: IntLike) -> Any:
return (
l if r == 0 else
r if l == 0 else
object.__new__(cls)
)
def __init__(self, l: IntLike, r: IntLike):
self.l = l
self.r = r
IntLike = Union[int, Add]
class RaisesException:
def __new__(cls, val: int) -> RaisesException:
if val == 0:
raise RuntimeError("Invalid value!")
return object.__new__(cls)
def __init__(self, val: int) -> None:
self.val = val
class ClsArgNotPassed:
def __new__(cls) -> Any:
return object.__new__(str)
class SkipsBase(Add):
def __new__(cls) -> Any:
obj = object.__new__(cls)
obj.l = 0
obj.r = 0
return obj
def test_dunder_new() -> None:
add_instance: Any = Add(1, 5)
assert type(add_instance) == Add
assert add_instance.l == 1
assert add_instance.r == 5
# TODO: explicit types should not be needed but mypy does not use
# the return type of __new__ which makes mypyc add casts to Add.
right_int: Any = Add(0, 5)
assert type(right_int) == int
assert right_int == 5
left_int: Any = Add(1, 0)
assert type(left_int) == int
assert left_int == 1
with assertRaises(RuntimeError, "Invalid value!"):
_ = RaisesException(0)
not_raised = RaisesException(1)
assert not_raised.val == 1
with assertRaises(TypeError, "object.__new__(str) is not safe, use str.__new__()"):
_ = ClsArgNotPassed()
skip = SkipsBase.__new__(SkipsBase)
assert type(skip) == SkipsBase
assert skip.l == 0
assert skip.r == 0
[case testObjectDunderNewInInterpreted]
from __future__ import annotations
from typing import Any, Union
class Add:
l: IntLike
r: IntLike
def __new__(cls, l: IntLike, r: IntLike) -> Any:
print(f'running __new__ with {l} and {r}')
return (
l if r == 0 else
r if l == 0 else
object.__new__(cls)
)
def __init__(self, l: IntLike, r: IntLike):
self.l = l
self.r = r
def __repr__(self) -> str:
return f'({self.l} + {self.r})'
IntLike = Union[int, Add]
class RaisesException:
def __new__(cls, val: int) -> RaisesException:
if val == 0:
raise RuntimeError("Invalid value!")
return object.__new__(cls)
def __init__(self, val: int) -> None:
self.val = val
class ClsArgNotPassed:
def __new__(cls) -> Any:
return object.__new__(str)
class SkipsBase(Add):
def __new__(cls) -> Any:
obj = object.__new__(cls)
obj.l = 0
obj.r = 0
return obj
[file driver.py]
from native import Add, ClsArgNotPassed, RaisesException, SkipsBase
from testutil import assertRaises
print(f'{Add(1, 5)=}')
print(f'{Add(0, 5)=}')
print(f'{Add(1, 0)=}')
with assertRaises(RuntimeError, "Invalid value!"):
raised = RaisesException(0)
not_raised = RaisesException(1)
assert not_raised.val == 1
with assertRaises(TypeError, "object.__new__(str) is not safe, use str.__new__()"):
str_as_cls = ClsArgNotPassed()
skip = SkipsBase.__new__(SkipsBase)
assert type(skip) == SkipsBase
assert skip.l == 0
assert skip.r == 0
[out]
running __new__ with 1 and 5
Add(1, 5)=(1 + 5)
running __new__ with 0 and 5
Add(0, 5)=5
running __new__ with 1 and 0
Add(1, 0)=1
[case testInheritedDunderNew]
from __future__ import annotations
from mypy_extensions import mypyc_attr
from testutil import assertRaises
from typing_extensions import Self
from m import interpreted_subclass
@mypyc_attr(allow_interpreted_subclasses=True)
class Base:
val: int
def __new__(cls, val: int) -> Self:
obj = super().__new__(cls)
obj.val = val + 1
return obj
def __init__(self, val: int) -> None:
self.init_val = val
def method(self) -> int:
raise NotImplementedError
class Sub(Base):
def __new__(cls, val: int) -> Self:
return super().__new__(cls, val + 1)
def __init__(self, val: int) -> None:
super().__init__(val)
self.init_val = self.init_val * 2
def method(self) -> int:
return 0
class SubWithoutNew(Base):
sub_only_str = ""
sub_only_int: int
def __init__(self, val: int) -> None:
super().__init__(val)
self.init_val = self.init_val * 2
def method(self) -> int:
return 1
class BaseWithoutInterpretedSubclasses:
val: int
def __new__(cls, val: int) -> Self:
obj = super().__new__(cls)
obj.val = val + 1
return obj
def __init__(self, val: int) -> None:
self.init_val = val
def method(self) -> int:
raise NotImplementedError
class SubNoInterpreted(BaseWithoutInterpretedSubclasses):
def __new__(cls, val: int) -> Self:
return super().__new__(cls, val + 1)
def __init__(self, val: int) -> None:
super().__init__(val)
self.init_val = self.init_val * 2
def method(self) -> int:
return 0
class SubNoInterpretedWithoutNew(BaseWithoutInterpretedSubclasses):
def __init__(self, val: int) -> None:
super().__init__(val)
self.init_val = self.init_val * 2
def method(self) -> int:
return 1
def test_inherited_dunder_new() -> None:
b = Base(42)
assert type(b) == Base
assert b.val == 43
assert b.init_val == 42
with assertRaises(NotImplementedError):
b.method()
s = Sub(42)
assert type(s) == Sub
assert s.val == 44
assert s.init_val == 84
assert s.method() == 0
s2 = SubWithoutNew(42)
assert type(s2) == SubWithoutNew
assert s2.val == 43
assert s2.init_val == 84
assert s2.method() == 1
assert s2.sub_only_str == ""
with assertRaises(AttributeError):
s2.sub_only_int
s2.sub_only_int = 11
assert s2.sub_only_int == 11
def test_inherited_dunder_new_without_interpreted_subclasses() -> None:
b = BaseWithoutInterpretedSubclasses(42)
assert type(b) == BaseWithoutInterpretedSubclasses
assert b.val == 43
assert b.init_val == 42
with assertRaises(NotImplementedError):
b.method()
s = SubNoInterpreted(42)
assert type(s) == SubNoInterpreted
assert s.val == 44
assert s.init_val == 84
assert s.method() == 0
s2 = SubNoInterpretedWithoutNew(42)
assert type(s2) == SubNoInterpretedWithoutNew
assert s2.val == 43
assert s2.init_val == 84
assert s2.method() == 1
def test_interpreted_subclass() -> None:
interpreted_subclass(Base)
[file m.py]
from __future__ import annotations
from testutil import assertRaises
from typing_extensions import Self
def interpreted_subclass(base) -> None:
b = base(42)
assert type(b) == base
assert b.val == 43
assert b.init_val == 42
with assertRaises(NotImplementedError):
b.method()
class InterpretedSub(base):
def __new__(cls, val: int) -> Self:
return super().__new__(cls, val + 1)
def __init__(self, val: int) -> None:
super().__init__(val)
self.init_val : int = self.init_val * 2
def method(self) -> int:
return 3
s = InterpretedSub(42)
assert type(s) == InterpretedSub
assert s.val == 44
assert s.init_val == 84
assert s.method() == 3
class InterpretedSubWithoutNew(base):
sub_only_str = ""
sub_only_int: int
def __init__(self, val: int) -> None:
super().__init__(val)
self.init_val : int = self.init_val * 2
def method(self) -> int:
return 4
s2 = InterpretedSubWithoutNew(42)
assert type(s2) == InterpretedSubWithoutNew
assert s2.val == 43
assert s2.init_val == 84
assert s2.method() == 4
assert s2.sub_only_str == ""
with assertRaises(AttributeError):
s2.sub_only_int
s2.sub_only_int = 11
assert s2.sub_only_int == 11
[typing fixtures/typing-full.pyi]
[case testDunderNewInitArgMismatch]
from __future__ import annotations
from testutil import assertRaises
class Test0:
@classmethod
def __new__(cls, val: int = 42) -> Test0:
obj = super().__new__(cls)
obj.val = val
return obj
def __init__(self) -> None:
self.val = 0
class Test1:
def __new__(cls, val: int) -> Test1:
obj = super().__new__(cls)
obj.val = val
return obj
def __init__(self) -> None:
self.val = 0
class Test2:
def __new__(cls) -> Test2:
obj = super().__new__(cls)
return obj
def __init__(self, val: int) -> None:
self.val = val
def test_arg_mismatch() -> None:
t0 = Test0()
assert t0.val == 0
t0 = Test0.__new__(1)
assert t0.val == 1
with assertRaises(TypeError, "__new__() missing required argument 'val'"):
t1 = Test1()
t1 = Test1.__new__(Test1, 2)
assert t1.val == 2
with assertRaises(TypeError, "__new__() takes at most 0 arguments"):
t2 = Test2(42)
t2 = Test2.__new__(Test2)
with assertRaises(AttributeError, "attribute 'val' of 'Test2' undefined"):
print(t2.val)
[case testDunderNewInitArgMismatchInInterpreted]
from __future__ import annotations
class Test0:
# TODO: It should be possible to annotate '@classmethod' here
# but when it's added calling __new__ in interpreted code
# without the explicit type param results in a TypeError.
def __new__(cls, val: int = 42) -> Test0:
obj = super().__new__(cls)
obj.val = val
return obj
def __init__(self) -> None:
self.val = 0
class Test1:
def __new__(cls, val: int) -> Test1:
obj = super().__new__(cls)
obj.val = val
return obj
def __init__(self) -> None:
self.val = 0
class Test2:
def __new__(cls) -> Test2:
obj = super().__new__(cls)
return obj
def __init__(self, val: int) -> None:
self.val = val
[file driver.py]
from native import Test0, Test1, Test2
from testutil import assertRaises
t0 = Test0()
assert t0.val == 0
t0 = Test0.__new__(Test0, 1)
assert t0.val == 1
with assertRaises(TypeError, "__new__() missing required argument 'val'"):
t1 = Test1()
t1 = Test1.__new__(Test1, 2)
assert t1.val == 2
with assertRaises(TypeError, "__new__() takes at most 0 arguments"):
t2 = Test2(42)
t2 = Test2.__new__(Test2)
with assertRaises(AttributeError, "attribute 'val' of 'Test2' undefined"):
print(t2.val)
[case testDunderNewAttributeAccess]
from __future__ import annotations
from mypy_extensions import u8
from testutil import assertRaises
class Test:
native: int
generic: object
bitfield: u8
default: int = 5
def __new__(cls, native: int, generic: object, bitfield: u8) -> Test:
obj = super().__new__(cls)
with assertRaises(AttributeError, "attribute 'native' of 'Test' undefined"):
print(obj.native)
with assertRaises(AttributeError, "attribute 'generic' of 'Test' undefined"):
print(obj.generic)
with assertRaises(AttributeError, "attribute 'bitfield' of 'Test' undefined"):
print(obj.bitfield)
obj.native = native
obj.generic = generic
obj.bitfield = bitfield
obj.native = obj.native + 1
obj.generic = obj.generic.__str__()
obj.bitfield = obj.bitfield & 0x0F
obj.default = obj.default * 2
return obj
def test_attribute_access() -> None:
t = Test(42, {}, 0xCC)
assert t.native == 43
assert t.generic == "{}"
assert t.bitfield == 0x0C
assert t.default == 10
[case testDunderNewAttributeAccessInInterpreted]
from __future__ import annotations
from mypy_extensions import u8
from testutil import assertRaises
class Test:
native: int
generic: object
bitfield: u8
default: int = 5
def __new__(cls, native: int, generic: object, bitfield: u8) -> Test:
obj = super().__new__(cls)
with assertRaises(AttributeError, "attribute 'native' of 'Test' undefined"):
print(obj.native)
with assertRaises(AttributeError, "attribute 'generic' of 'Test' undefined"):
print(obj.generic)
with assertRaises(AttributeError, "attribute 'bitfield' of 'Test' undefined"):
print(obj.bitfield)
obj.native = native
obj.generic = generic
obj.bitfield = bitfield
obj.native = obj.native + 1
obj.generic = obj.generic.__str__()
obj.bitfield = obj.bitfield & 0x0F
obj.default = obj.default * 2
return obj
[file driver.py]
from native import Test
t = Test(42, {}, 0xCC)
assert t.native == 43
assert t.generic == "{}"
assert t.bitfield == 0x0C
assert t.default == 10
[case testUntransformedDunderNewCalls]
from testutil import assertRaises
from typing import Any
class TestStrCls:
def __new__(cls):
return str.__new__(cls)
@classmethod
def factory(cls):
return str.__new__(cls)
class TestStrStr:
def __new__(cls):
return str.__new__(str)
@classmethod
def factory(cls):
return str.__new__(str)
class TestStrInt:
def __new__(cls):
return str.__new__(int)
@classmethod
def factory(cls):
return str.__new__(int)
def test_untransformed_dunder_new() -> None:
with assertRaises(TypeError, "str.__new__(TestStrCls): TestStrCls is not a subtype of str"):
i = TestStrCls()
j: Any = TestStrStr()
assert j == ""
with assertRaises(TypeError, "str.__new__(int): int is not a subtype of str"):
k = TestStrInt()
with assertRaises(TypeError, "str.__new__(TestStrCls): TestStrCls is not a subtype of str"):
i = TestStrCls.factory()
j = TestStrStr.factory()
assert j == ""
with assertRaises(TypeError, "str.__new__(int): int is not a subtype of str"):
k = TestStrInt.factory()
[case testDecoratedDunderNew]
from __future__ import annotations
from typing import Any, Callable, TypeVar, overload
from mypy_extensions import mypyc_attr
from testutil import assertRaises
from typing_extensions import Self
from m import check_interpreted_subclasses
F = TypeVar("F", bound=Callable[..., Any])
log: list[str] = []
def take() -> list[str]:
result = list(log)
log.clear()
return result
def logged(f: F) -> F:
def wrapper(cls: Any, *args: Any, **kwargs: Any) -> Any:
log.append(f"before {cls.__name__} {args} {kwargs}")
obj = f(cls, *args, **kwargs)
log.append("after")
return obj
return wrapper # type: ignore[return-value]
def tagged(tag: str) -> Callable[[F], F]:
def deco(f: F) -> F:
def wrapper(*args: Any) -> Any:
log.append(tag)
return f(*args)
return wrapper # type: ignore[return-value]
return deco
def unchanged(f: F) -> F:
return f
def non_negative(f: F) -> F:
def wrapper(cls: Any, x: int) -> Any:
if x < 0:
raise ValueError("negative")
return f(cls, x)
return wrapper # type: ignore[return-value]
class Point:
@logged
def __new__(cls, x: int, y: int = 0) -> Self:
log.append(f"new {x} {y}")
return super().__new__(cls)
def __init__(self, x: int, y: int = 0) -> None:
log.append(f"init {x} {y}")
self.x = x
self.y = y
self.label = f"{x},{y}"
class PointChild(Point):
pass
class PointChildWithInit(Point):
def __init__(self, x: int, y: int = 0) -> None:
super().__init__(x + 10, y + 10)
log.append("child init")
def test_new_and_init() -> None:
p = Point(1)
assert take() == ["before Point (1,) {}", "new 1 0", "after", "init 1 0"]
assert (p.x, p.y) == (1, 0)
p = Point(2, y=3)
assert take() == ["before Point (2,) {'y': 3}", "new 2 3", "after", "init 2 3"]
assert (p.x, p.y) == (2, 3)
# Use Any to get non-native semantics
cls: Any = Point
p = cls(4, 5)
assert take() == ["before Point (4, 5) {}", "new 4 5", "after", "init 4 5"]
assert (p.x, p.y) == (4, 5)
with assertRaises(TypeError):
cls()
assert take() == ["before Point () {}"]
def test_inherited_new() -> None:
c = PointChild(1, 2)
assert take() == ["before PointChild (1, 2) {}", "new 1 2", "after", "init 1 2"]
assert type(c) is PointChild
assert (c.x, c.y) == (1, 2)
d = PointChildWithInit(1, 2)
assert take() == [
"before PointChildWithInit (1, 2) {}",
"new 1 2",
"after",
"init 11 12",
"child init",
]
assert type(d) is PointChildWithInit
assert (d.x, d.y) == (11, 12)
cls: Any = PointChild
c = cls(3)
assert take() == ["before PointChild (3,) {}", "new 3 0", "after", "init 3 0"]
assert type(c) is PointChild
cls = PointChildWithInit
d = cls(3)
assert take() == [
"before PointChildWithInit (3,) {}",
"new 3 0",
"after",
"init 13 10",
"child init",
]
assert d.x == 13
def test_explicit_call_does_not_call_init() -> None:
p = Point.__new__(Point, 1)
assert take() == ["before Point (1,) {}", "new 1 0", "after"]
assert type(p) is Point
with assertRaises(AttributeError):
p.x
with assertRaises(AttributeError):
p.label
c = Point.__new__(PointChild, 2)
assert take() == ["before PointChild (2,) {}", "new 2 0", "after"]
assert type(c) is PointChild
cls: Any = Point
p = cls.__new__(cls, 3, y=4)
assert take() == ["before Point (3,) {'y': 4}", "new 3 4", "after"]
with assertRaises(AttributeError):
p.label
assert type(cls.__dict__["__new__"]) is staticmethod
class Root:
def __init__(self, x: int) -> None:
log.append(f"init {x}")
self.x = x
class Mid(Root):
@logged
def __new__(cls, x: int) -> Self:
log.append("new")
return super().__new__(cls)
class Leaf(Mid):
pass
def test_init_from_base_class() -> None:
# Root doesn't define __new__, so its tp_new calls __init__, and its tp_init
# does nothing. The subclasses can't use that tp_init.
for cls in Mid, Leaf:
any_cls: Any = cls
obj = any_cls(1)
assert take() == [f"before {cls.__name__} (1,) {{}}", "new", "after", "init 1"]
assert obj.x == 1
m = Mid(2)
assert take() == ["before Mid (2,) {}", "new", "after", "init 2"]
assert m.x == 2
l = Leaf(3)
assert take() == ["before Leaf (3,) {}", "new", "after", "init 3"]
assert l.x == 3
r = Root(4)
assert take() == ["init 4"]
assert r.x == 4
any_cls = Root
r = any_cls(5)
assert take() == ["init 5"]
class TwoDecorators:
@tagged("outer")
@tagged("inner")
def __new__(cls) -> Self:
log.append("new")
return super().__new__(cls)
class Unchanged:
@unchanged
def __new__(cls) -> Self:
log.append("new")
return super().__new__(cls)
def test_other_decorators() -> None:
TwoDecorators()
assert take() == ["outer", "inner", "new"]
Unchanged()
assert take() == ["new"]
cls: Any = TwoDecorators
cls()
assert take() == ["outer", "inner", "new"]
cls = Unchanged
cls()
cls.__new__(cls)
assert take() == ["new", "new"]
class Overloaded:
@overload
def __new__(cls, x: int) -> Overloaded: ...
@overload
def __new__(cls, x: str) -> Overloaded: ...
@logged
def __new__(cls, x: object) -> Overloaded:
log.append(f"new {x}")
return super().__new__(cls)
def __init__(self, x: object) -> None:
self.x = x
def test_overloaded_new_with_decorated_implementation() -> None:
o = Overloaded(1)
assert take() == ["before Overloaded (1,) {}", "new 1", "after"]
assert o.x == 1
cls: Any = Overloaded
o = cls("s")
assert take() == ["before Overloaded ('s',) {}", "new s", "after"]
assert o.x == "s"
class NotAlwaysInstance:
@logged
def __new__(cls, x: int) -> Any:
if x == 0:
return "zero"
return super().__new__(cls)
def __init__(self, x: int) -> None:
log.append("init")
self.x = x
def test_new_returns_instance_of_other_class() -> None:
a: Any = NotAlwaysInstance(0)
assert a == "zero"
assert take() == ["before NotAlwaysInstance (0,) {}", "after"]
b: Any = NotAlwaysInstance(1)
assert b.x == 1
assert take() == ["before NotAlwaysInstance (1,) {}", "after", "init"]
cls: Any = NotAlwaysInstance
assert cls(0) == "zero"
assert take() == ["before NotAlwaysInstance (0,) {}", "after"]
class Checked:
@non_negative
def __new__(cls, x: int) -> Self:
if x == 0:
raise RuntimeError("zero")
return super().__new__(cls)
def __init__(self, x: int) -> None:
if x == 1:
raise KeyError("one")
self.x = x
def test_exceptions() -> None:
any_cls: Any = Checked
for cls in Checked, any_cls:
with assertRaises(ValueError, "negative"):
cls(-1)
with assertRaises(RuntimeError, "zero"):
cls(0)
with assertRaises(KeyError, "one"):
cls(1)
assert cls(2).x == 2
@mypyc_attr(allow_interpreted_subclasses=True)
class Open:
@logged
def __new__(cls, x: int) -> Self:
return super().__new__(cls)
def __init__(self, x: int) -> None:
self.x = x
def value(self) -> int:
return self.x
def call_value(o: Open) -> int:
return o.value()
def test_interpreted_subclasses() -> None:
o = Open(1)
assert take() == ["before Open (1,) {}", "after"]
assert call_value(o) == 1
check_interpreted_subclasses()
[file m.py]
from testutil import assertRaises
def check_interpreted_subclasses() -> None:
from native import Leaf, Open, Point, PointChild, call_value, take
class OpenChild(Open):
def value(self) -> int:
return self.x + 100
o = OpenChild(1)
assert take() == ["before OpenChild (1,) {}", "after"]
assert o.x == 1
assert call_value(o) == 101
class OpenChildWithNew(Open):
y: int
def __new__(cls, x):
obj = super().__new__(cls, x + 1)
obj.y = 5
return obj
def __init__(self, x):
super().__init__(x + 2)
o2 = OpenChildWithNew(1)
assert take() == ["before OpenChildWithNew (2,) {}", "after"]
assert (o2.x, o2.y) == (3, 5)
assert call_value(o2) == 3
# The other classes don't allow interpreted subclasses.
for base in Point, PointChild, Leaf:
class Child(base): # type: ignore[misc, valid-type]
pass
with assertRaises(TypeError, "interpreted classes cannot inherit from compiled"):
Child(1)
take()
class ChildWithNew(base): # type: ignore[misc, valid-type]
def __new__(cls, x):
return super().__new__(cls, x)
with assertRaises(TypeError, "interpreted classes cannot inherit from compiled"):
ChildWithNew(1)
take()
[typing fixtures/typing-full.pyi]
[case testDecoratedDunderNewWithOtherArgumentsThanInit]
from __future__ import annotations
import functools
from typing import Any, Callable, TypeVar
from typing_extensions import Self
F = TypeVar("F", bound=Callable[..., Any])
log: list[str] = []
def take() -> list[str]:
result = list(log)
log.clear()
return result
def logged(f: F) -> F:
def wrapper(cls: Any, *args: Any, **kwargs: Any) -> Any:
log.append(f"before {cls.__name__} {args} {kwargs}")
obj = f(cls, *args, **kwargs)
log.append("after")
return obj
return wrapper # type: ignore[return-value]
class NoInit:
x: int
@logged
def __new__(cls, x: int) -> Self:
obj = super().__new__(cls)
obj.x = x + 1
return obj
def test_no_init() -> None:
n = NoInit(1)
assert take() == ["before NoInit (1,) {}", "after"]
assert n.x == 2
cls: Any = NoInit
n = cls(x=2)
assert take() == ["before NoInit () {'x': 2}", "after"]
assert n.x == 3
class AnyArguments:
@logged
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
log.append("new")
return super().__new__(cls)
def __init__(self, x: int, y: int = 0) -> None:
log.append(f"init {x} {y}")
self.x = x
self.y = y
class AnyArgumentsChild(AnyArguments):
def __init__(self, x: int) -> None:
super().__init__(x, 10)
def test_any_arguments() -> None:
a = AnyArguments(1, y=2)
assert take() == ["before AnyArguments (1,) {'y': 2}", "new", "after", "init 1 2"]
assert (a.x, a.y) == (1, 2)
c = AnyArgumentsChild(3)
assert take() == ["before AnyArgumentsChild (3,) {}", "new", "after", "init 3 10"]
assert (c.x, c.y) == (3, 10)
cls: Any = AnyArguments
a = cls(4)
assert take() == ["before AnyArguments (4,) {}", "new", "after", "init 4 0"]
assert (a.x, a.y) == (4, 0)
class Interned:
name: str
@functools.cache # type: ignore[misc]
def __new__(cls, name: str) -> Self:
log.append(f"new {name}")
obj = super().__new__(cls)
obj.name = name
return obj
def test_cache() -> None:
a = Interned("a")
b = Interned("b")
assert Interned("a") is a
cls: Any = Interned
assert cls("b") is b
assert take() == ["new a", "new b"]
assert (a.name, b.name) == ("a", "b")
assert cls.__new__.cache_info().hits == 2
[typing fixtures/typing-full.pyi]
[case testDecoratedDunderNewWithBuiltInBaseClass]
from typing import Any, Callable, TypeVar
F = TypeVar("F", bound=Callable[..., Any])
log: list[str] = []
def logged(f: F) -> F:
def wrapper(cls: Any, *args: Any) -> Any:
log.append(f"before {cls.__name__} {args}")
obj = f(cls, *args)
log.append("after")
return obj
return wrapper # type: ignore[return-value]
class Table(dict): # type: ignore[type-arg]
@logged
def __new__(cls, *args: Any) -> Any:
log.append("new")
return dict.__new__(cls)
class Error(Exception):
@logged
def __new__(cls, *args: Any) -> Any:
log.append("new")
return Exception.__new__(cls, *args)
def __init__(self, x: int) -> None:
log.append("init")
super().__init__(str(x + 1))
def test_built_in_base_class() -> None:
t = Table({"a": 1})
assert log == ["before Table ({'a': 1},)", "new", "after"]
assert type(t) is Table
assert t == {"a": 1}
log.clear()
e = Error(1)
assert log == ["before Error (1,)", "new", "after", "init"]
assert type(e) is Error
assert str(e) == "2"
[case testPerTypeFreeList]
from __future__ import annotations
from mypy_extensions import mypyc_attr
a: list[int] = []
@mypyc_attr(free_list_len=1)
class Foo:
def __init__(self, x: int) -> None:
self.x = x
a.append(x)
def test_alloc() -> None:
x: Foo | None
y: Foo | None
x = Foo(1)
assert x.x == 1
x = None
x = Foo(2)
assert x.x == 2
y = Foo(3)
assert x.x == 2
assert y.x == 3
x = None
y = None
assert a == [1, 2, 3]
x = Foo(4)
assert x.x == 4
y = Foo(5)
assert x.x == 4
assert y.x == 5
@mypyc_attr(free_list_len=1)
class Base:
def __init__(self, x: str) -> None:
self.x = x
class Deriv(Base):
def __init__(self, x: str, y: str) -> None:
super().__init__(x)
self.y = y
@mypyc_attr(free_list_len=1)
class Deriv2(Base):
def __init__(self, x: str, y: str) -> None:
super().__init__(x)
self.y = y
def test_inheritance() -> None:
x: Base | None
y: Base | None
x = Base('x' + str())
y = Base('y' + str())
y = None
d = Deriv('a' + str(), 'b' + str())
assert type(d) is Deriv
assert d.x == 'a'
assert d.y == 'b'
assert x.x == 'x'
y = Base('z' + str())
assert d.x == 'a'
assert d.y == 'b'
assert y.x == 'z'
x = None
y = None
def test_inheritance_2() -> None:
x: Base | None
y: Base | None
d: Deriv2 | None
x = Base('x' + str())
y = Base('y' + str())
y = None
d = Deriv2('a' + str(), 'b' + str())
assert type(d) is Deriv2
assert d.x == 'a'
assert d.y == 'b'
assert x.x == 'x'
d = None
d = Deriv2('c' + str(), 'd' + str())
assert type(d) is Deriv2
assert d.x == 'c'
assert d.y == 'd'
assert x.x == 'x'
y = Base('z' + str())
assert type(y) is Base
assert d.x == 'c'
assert d.y == 'd'
assert y.x == 'z'
x = None
y = None
d = None
[case testDunderGetAttr]
from mypy_extensions import mypyc_attr
from typing import ClassVar
class GetAttr:
class_var = "x"
def __init__(self, extra_attrs: dict[str, object], regular_attr: int):
self.extra_attrs = extra_attrs
self.regular_attr = regular_attr
def __getattr__(self, attr: str) -> object:
return self.extra_attrs.get(attr)
class GetAttrDefault:
class_var: ClassVar[str] = "x"
def __init__(self, extra_attrs: dict[str, object], regular_attr: int):
self.extra_attrs = extra_attrs
self.regular_attr = regular_attr
def __getattr__(self, attr: str, default: int = 8, mult: int = 1) -> object:
return self.extra_attrs.get(attr, default * mult)
class GetAttrInherited(GetAttr):
subclass_var = "y"
def __init__(self, extra_attrs: dict[str, object], regular_attr: int, sub_attr: int):
super().__init__(extra_attrs, regular_attr)
self.sub_attr = sub_attr
class GetAttrOverridden(GetAttr):
subclass_var: ClassVar[str] = "y"
def __init__(self, extra_attrs: dict[str, object], regular_attr: int, sub_attr: int):
super().__init__(extra_attrs, regular_attr)
self.sub_attr = sub_attr
def __getattr__(self, attr: str) -> str:
return attr
@mypyc_attr(native_class=False)
class GetAttrNonNative:
class_var = "x"
def __init__(self, extra_attrs: dict[str, object], regular_attr: int):
self.extra_attrs = extra_attrs
self.regular_attr = regular_attr
def __getattr__(self, attr: str) -> object:
return self.extra_attrs.get(attr)
def test_getattr() -> None:
i = GetAttr({"one": 1, "two": "two", "three": 3.14}, 42)
assert i.__getattr__("one") == 1
assert i.__getattr__("regular_attr") == None
assert i.__getattr__("class_var") == None
assert i.__getattr__("four") == None
assert getattr(i, "two") == "two"
assert getattr(i, "regular_attr") == 42
assert getattr(i, "class_var") == "x"
assert getattr(i, "four") == None
assert i.three == 3.14
assert i.regular_attr == 42
assert i.class_var == "x"
assert i.four == None
assert i.__class__ == GetAttr
i.extra_attrs["regular_attr"] = (4, 4, 4)
assert i.__getattr__("regular_attr") == (4, 4, 4)
assert getattr(i, "regular_attr") == 42
assert i.regular_attr == 42
def test_getattr_default() -> None:
i = GetAttrDefault({"one": 1, "two": "two", "three": 3.14}, 42)
assert i.__getattr__("one") == 1
assert i.__getattr__("regular_attr") == 8
assert i.__getattr__("class_var") == 8
assert i.__getattr__("four", 4, 3) == 12
assert getattr(i, "two") == "two"
assert getattr(i, "regular_attr") == 42
assert getattr(i, "class_var") == "x"
assert getattr(i, "four") == 8
assert i.three == 3.14
assert i.regular_attr == 42
assert i.class_var == "x"
assert i.four == 8
assert i.__class__ == GetAttrDefault
i.extra_attrs["class_var"] = (4, 4, 4)
assert i.__getattr__("class_var") == (4, 4, 4)
assert getattr(i, "class_var") == "x"
assert i.class_var == "x"
def test_getattr_inherited() -> None:
i = GetAttrInherited({"one": 1, "two": "two", "three": 3.14}, 42, 24)
assert i.__getattr__("one") == 1
assert i.__getattr__("regular_attr") == None
assert i.__getattr__("sub_attr") == None
assert i.__getattr__("class_var") == None
assert i.__getattr__("subclass_var") == None
assert i.__getattr__("four") == None
assert getattr(i, "two") == "two"
assert getattr(i, "regular_attr") == 42
assert getattr(i, "sub_attr") == 24
assert getattr(i, "class_var") == "x"
assert getattr(i, "subclass_var") == "y"
assert getattr(i, "four") == None
assert i.three == 3.14
assert i.regular_attr == 42
assert i.sub_attr == 24
assert i.class_var == "x"
assert i.subclass_var == "y"
assert i.four == None
assert i.__class__ == GetAttrInherited
i.extra_attrs["sub_attr"] = (4, 4, 4)
assert i.__getattr__("sub_attr") == (4, 4, 4)
assert getattr(i, "sub_attr") == 24
assert i.sub_attr == 24
base_ref: GetAttr = i
assert getattr(base_ref, "sub_attr") == 24
assert base_ref.sub_attr == 24
assert getattr(base_ref, "subclass_var") == "y"
assert base_ref.subclass_var == "y"
assert getattr(base_ref, "new") == None
assert base_ref.new == None
assert base_ref.__class__ == GetAttrInherited
def test_getattr_overridden() -> None:
i = GetAttrOverridden({"one": 1, "two": "two", "three": 3.14}, 42, 24)
assert i.__getattr__("one") == "one"
assert i.__getattr__("regular_attr") == "regular_attr"
assert i.__getattr__("sub_attr") == "sub_attr"
assert i.__getattr__("class_var") == "class_var"
assert i.__getattr__("subclass_var") == "subclass_var"
assert i.__getattr__("four") == "four"
assert getattr(i, "two") == "two"
assert getattr(i, "regular_attr") == 42
assert getattr(i, "sub_attr") == 24
assert getattr(i, "class_var") == "x"
assert getattr(i, "subclass_var") == "y"
assert getattr(i, "four") == "four"
assert i.three == "three"
assert i.regular_attr == 42
assert i.sub_attr == 24
assert i.class_var == "x"
assert i.subclass_var == "y"
assert i.four == "four"
assert i.__class__ == GetAttrOverridden
i.extra_attrs["subclass_var"] = (4, 4, 4)
assert i.__getattr__("subclass_var") == "subclass_var"
assert getattr(i, "subclass_var") == "y"
assert i.subclass_var == "y"
base_ref: GetAttr = i
assert getattr(base_ref, "sub_attr") == 24
assert base_ref.sub_attr == 24
assert getattr(base_ref, "subclass_var") == "y"
assert base_ref.subclass_var == "y"
assert getattr(base_ref, "new") == "new"
assert base_ref.new == "new"
assert base_ref.__class__ == GetAttrOverridden
def test_getattr_nonnative() -> None:
i = GetAttr({"one": 1, "two": "two", "three": 3.14}, 42)
assert i.__getattr__("one") == 1
assert i.__getattr__("regular_attr") == None
assert i.__getattr__("class_var") == None
assert i.__getattr__("four") == None
assert getattr(i, "two") == "two"
assert getattr(i, "regular_attr") == 42
assert getattr(i, "class_var") == "x"
assert getattr(i, "four") == None
assert i.three == 3.14
assert i.regular_attr == 42
assert i.class_var == "x"
assert i.four == None
assert i.__class__ == GetAttr
i.extra_attrs["regular_attr"] = (4, 4, 4)
assert i.__getattr__("regular_attr") == (4, 4, 4)
assert getattr(i, "regular_attr") == 42
assert i.regular_attr == 42
[typing fixtures/typing-full.pyi]
[case testDunderGetAttrInterpreted]
from mypy_extensions import mypyc_attr
from typing import ClassVar
class GetAttr:
class_var = "x"
def __init__(self, extra_attrs: dict[str, object], regular_attr: int):
self.extra_attrs = extra_attrs
self.regular_attr = regular_attr
def __getattr__(self, attr: str) -> object:
return self.extra_attrs.get(attr)
class GetAttrDefault:
class_var: ClassVar[str] = "x"
def __init__(self, extra_attrs: dict[str, object], regular_attr: int):
self.extra_attrs = extra_attrs
self.regular_attr = regular_attr
def __getattr__(self, attr: str, default: int = 8, mult: int = 1) -> object:
return self.extra_attrs.get(attr, default * mult)
class GetAttrInherited(GetAttr):
subclass_var = "y"
def __init__(self, extra_attrs: dict[str, object], regular_attr: int, sub_attr: int):
super().__init__(extra_attrs, regular_attr)
self.sub_attr = sub_attr
class GetAttrOverridden(GetAttr):
subclass_var: ClassVar[str] = "y"
def __init__(self, extra_attrs: dict[str, object], regular_attr: int, sub_attr: int):
super().__init__(extra_attrs, regular_attr)
self.sub_attr = sub_attr
def __getattr__(self, attr: str) -> str:
return attr
@mypyc_attr(native_class=False)
class GetAttrNonNative:
class_var = "x"
def __init__(self, extra_attrs: dict[str, object], regular_attr: int):
self.extra_attrs = extra_attrs
self.regular_attr = regular_attr
def __getattr__(self, attr: str) -> object:
return self.extra_attrs.get(attr)
[file driver.py]
from native import GetAttr, GetAttrDefault, GetAttrInherited, GetAttrOverridden, GetAttrNonNative
def test_getattr() -> None:
i = GetAttr({"one": 1, "two": "two", "three": 3.14}, 42)
assert i.__getattr__("one") == 1
assert i.__getattr__("regular_attr") == None
assert i.__getattr__("class_var") == None
assert i.__getattr__("four") == None
assert getattr(i, "two") == "two"
assert getattr(i, "regular_attr") == 42
assert getattr(i, "class_var") == "x"
assert getattr(i, "four") == None
assert i.three == 3.14
assert i.regular_attr == 42
assert i.class_var == "x"
assert i.four == None
assert i.__class__ == GetAttr
i.extra_attrs["regular_attr"] = (4, 4, 4)
assert i.__getattr__("regular_attr") == (4, 4, 4)
assert getattr(i, "regular_attr") == 42
assert i.regular_attr == 42
def test_getattr_default() -> None:
i = GetAttrDefault({"one": 1, "two": "two", "three": 3.14}, 42)
assert i.__getattr__("one") == 1
assert i.__getattr__("regular_attr") == 8
assert i.__getattr__("class_var") == 8
assert i.__getattr__("four", 4, 3) == 12
assert getattr(i, "two") == "two"
assert getattr(i, "regular_attr") == 42
assert getattr(i, "class_var") == "x"
assert getattr(i, "four") == 8
assert i.three == 3.14
assert i.regular_attr == 42
assert i.class_var == "x"
assert i.four == 8
assert i.__class__ == GetAttrDefault
i.extra_attrs["class_var"] = (4, 4, 4)
assert i.__getattr__("class_var") == (4, 4, 4)
assert getattr(i, "class_var") == "x"
assert i.class_var == "x"
def test_getattr_inherited() -> None:
i = GetAttrInherited({"one": 1, "two": "two", "three": 3.14}, 42, 24)
assert i.__getattr__("one") == 1
assert i.__getattr__("regular_attr") == None
assert i.__getattr__("sub_attr") == None
assert i.__getattr__("class_var") == None
assert i.__getattr__("subclass_var") == None
assert i.__getattr__("four") == None
assert getattr(i, "two") == "two"
assert getattr(i, "regular_attr") == 42
assert getattr(i, "sub_attr") == 24
assert getattr(i, "class_var") == "x"
assert getattr(i, "subclass_var") == "y"
assert getattr(i, "four") == None
assert i.three == 3.14
assert i.regular_attr == 42
assert i.sub_attr == 24
assert i.class_var == "x"
assert i.subclass_var == "y"
assert i.four == None
assert i.__class__ == GetAttrInherited
i.extra_attrs["sub_attr"] = (4, 4, 4)
assert i.__getattr__("sub_attr") == (4, 4, 4)
assert getattr(i, "sub_attr") == 24
assert i.sub_attr == 24
base_ref: GetAttr = i
assert getattr(base_ref, "sub_attr") == 24
assert base_ref.sub_attr == 24
assert getattr(base_ref, "subclass_var") == "y"
assert base_ref.subclass_var == "y"
assert getattr(base_ref, "new") == None
assert base_ref.new == None
assert base_ref.__class__ == GetAttrInherited
def test_getattr_overridden() -> None:
i = GetAttrOverridden({"one": 1, "two": "two", "three": 3.14}, 42, 24)
assert i.__getattr__("one") == "one"
assert i.__getattr__("regular_attr") == "regular_attr"
assert i.__getattr__("sub_attr") == "sub_attr"
assert i.__getattr__("class_var") == "class_var"
assert i.__getattr__("subclass_var") == "subclass_var"
assert i.__getattr__("four") == "four"
assert getattr(i, "two") == "two"
assert getattr(i, "regular_attr") == 42
assert getattr(i, "sub_attr") == 24
assert getattr(i, "class_var") == "x"
assert getattr(i, "subclass_var") == "y"
assert getattr(i, "four") == "four"
assert i.three == "three"
assert i.regular_attr == 42
assert i.sub_attr == 24
assert i.class_var == "x"
assert i.subclass_var == "y"
assert i.four == "four"
assert i.__class__ == GetAttrOverridden
i.extra_attrs["subclass_var"] = (4, 4, 4)
assert i.__getattr__("subclass_var") == "subclass_var"
assert getattr(i, "subclass_var") == "y"
assert i.subclass_var == "y"
base_ref: GetAttr = i
assert getattr(base_ref, "sub_attr") == 24
assert base_ref.sub_attr == 24
assert getattr(base_ref, "subclass_var") == "y"
assert base_ref.subclass_var == "y"
assert getattr(base_ref, "new") == "new"
assert base_ref.new == "new"
assert base_ref.__class__ == GetAttrOverridden
def test_getattr_nonnative() -> None:
i = GetAttr({"one": 1, "two": "two", "three": 3.14}, 42)
assert i.__getattr__("one") == 1
assert i.__getattr__("regular_attr") == None
assert i.__getattr__("class_var") == None
assert i.__getattr__("four") == None
assert getattr(i, "two") == "two"
assert getattr(i, "regular_attr") == 42
assert getattr(i, "class_var") == "x"
assert getattr(i, "four") == None
assert i.three == 3.14
assert i.regular_attr == 42
assert i.class_var == "x"
assert i.four == None
assert i.__class__ == GetAttr
i.extra_attrs["regular_attr"] = (4, 4, 4)
assert i.__getattr__("regular_attr") == (4, 4, 4)
assert getattr(i, "regular_attr") == 42
assert i.regular_attr == 42
test_getattr()
test_getattr_default()
test_getattr_inherited()
test_getattr_overridden()
test_getattr_nonnative()
[typing fixtures/typing-full.pyi]
[case testDunderSetAttr]
from mypy_extensions import mypyc_attr
from testutil import assertRaises
from typing import ClassVar
class SetAttr:
_attributes: dict[str, object]
regular_attr: int
class_var: ClassVar[str] = "x"
const: int = 42
def __init__(self, regular_attr: int, extra_attrs: dict[str, object]) -> None:
super().__setattr__("_attributes", extra_attrs)
super().__setattr__("regular_attr", regular_attr)
def __setattr__(self, key: str, val: object) -> None:
if key == "regular_attr":
super().__setattr__("regular_attr", val)
elif key == "class_var" or key == "const":
raise AttributeError()
else:
self._attributes[key] = val
def __getattr__(self, key: str) -> object:
return self._attributes.get(key)
class SetAttrInherited(SetAttr):
def __init__(self, regular_attr: int, extra_attrs: dict[str, object]) -> None:
super().__init__(regular_attr, extra_attrs)
class SetAttrOverridden(SetAttr):
sub_attr: int
subclass_var: ClassVar[str] = "y"
def __init__(self, regular_attr: int, sub_attr: int, extra_attrs: dict[str, object]) -> None:
super().__init__(regular_attr, extra_attrs)
object.__setattr__(self, "sub_attr", sub_attr)
def __setattr__(self, key: str, val: object) -> None:
if key == "sub_attr":
object.__setattr__(self, "sub_attr", val)
elif key == "subclass_var":
raise AttributeError()
else:
super().__setattr__(key, val)
def __delattr__(self, key: str) -> None:
del self._attributes[key]
@mypyc_attr(native_class=False)
class SetAttrNonNative:
_attributes: dict[str, object]
regular_attr: int
class_var: ClassVar[str] = "x"
const: int = 42
def __init__(self, regular_attr: int, extra_attrs: dict[str, object]) -> None:
super().__setattr__("_attributes", extra_attrs)
super().__setattr__("regular_attr", regular_attr)
def __setattr__(self, key: str, val: object) -> None:
if key == "regular_attr":
super().__setattr__("regular_attr", val)
elif key == "class_var" or key == "const":
raise AttributeError()
else:
self._attributes[key] = val
def __getattr__(self, key: str) -> object:
return self._attributes.get(key)
class NoSetAttr:
def __init__(self, attr: int) -> None:
self.attr = attr
def object_setattr(self, attr: str, val: object) -> None:
object.__setattr__(self, attr, val)
def super_setattr(self, attr: str, val: object) -> None:
super().__setattr__(attr, val)
@mypyc_attr(native_class=False)
class NoSetAttrNonNative:
def __init__(self, attr: int) -> None:
self.attr = attr
def object_setattr(self, attr: str, val: object) -> None:
object.__setattr__(self, attr, val)
def super_setattr(self, attr: str, val: object) -> None:
super().__setattr__(attr, val)
def __getattr__(self, attr: str) -> object:
pass
def test_setattr() -> None:
i = SetAttr(99, {"one": 1})
assert i.class_var == "x"
assert i.regular_attr == 99
assert i.one == 1
assert i.two == None
assert i.const == 42
i = i
i.__setattr__("two", "2")
assert i.two == "2"
i.__setattr__("regular_attr", 101)
assert i.regular_attr == 101
with assertRaises(AttributeError):
i.__setattr__("class_var", "y")
with assertRaises(AttributeError):
i.__setattr__("const", 43)
setattr(i, "three", (3,3,3))
assert i.three == (3,3,3)
setattr(i, "regular_attr", 102)
assert i.regular_attr == 102
with assertRaises(AttributeError):
setattr(i, "class_var", "z")
with assertRaises(AttributeError):
setattr(i, "const", 44)
i.four = [4,4]
assert i.four == [4,4]
i.regular_attr = 103
assert i.regular_attr == 103
with assertRaises(AttributeError):
i.const = 45
# Doesn't work because there's no __delattr__.
with assertRaises(AttributeError):
del i.four
def test_setattr_inherited() -> None:
i = SetAttrInherited(99, {"one": 1})
assert i.class_var == "x"
assert i.regular_attr == 99
assert i.one == 1
assert i.two == None
assert i.const == 42
i = i
i.__setattr__("two", "2")
assert i.two == "2"
i.__setattr__("regular_attr", 101)
assert i.regular_attr == 101
with assertRaises(AttributeError):
i.__setattr__("class_var", "y")
with assertRaises(AttributeError):
i.__setattr__("const", 43)
setattr(i, "three", (3,3,3))
assert i.three == (3,3,3)
setattr(i, "regular_attr", 102)
assert i.regular_attr == 102
with assertRaises(AttributeError):
setattr(i, "class_var", "z")
with assertRaises(AttributeError):
setattr(i, "const", 44)
i.four = [4,4]
assert i.four == [4,4]
i.regular_attr = 103
assert i.regular_attr == 103
with assertRaises(AttributeError):
i.const = 45
# Doesn't work because there's no __delattr__.
with assertRaises(AttributeError):
del i.four
def test_setattr_overridden() -> None:
i = SetAttrOverridden(99, 1, {"one": 1})
assert i.class_var == "x"
assert i.subclass_var == "y"
assert i.regular_attr == 99
assert i.sub_attr == 1
assert i.one == 1
assert i.two == None
assert i.const == 42
i = i
i.__setattr__("two", "2")
assert i.two == "2"
i.__setattr__("regular_attr", 101)
assert i.regular_attr == 101
i.__setattr__("sub_attr", 2)
assert i.sub_attr == 2
with assertRaises(AttributeError):
i.__setattr__("class_var", "y")
with assertRaises(AttributeError):
i.__setattr__("subclass_var", "a")
with assertRaises(AttributeError):
i.__setattr__("const", 43)
setattr(i, "three", (3,3,3))
assert i.three == (3,3,3)
setattr(i, "regular_attr", 102)
assert i.regular_attr == 102
setattr(i, "sub_attr", 3)
assert i.sub_attr == 3
with assertRaises(AttributeError):
setattr(i, "class_var", "z")
with assertRaises(AttributeError):
setattr(i, "subclass_var", "b")
with assertRaises(AttributeError):
setattr(i, "const", 44)
i.four = [4,4]
assert i.four == [4,4]
i.regular_attr = 103
assert i.regular_attr == 103
i.sub_attr = 4
assert i.sub_attr == 4
with assertRaises(AttributeError):
i.const = 45
del i.four
assert "four" not in i._attributes
delattr(i, "three")
assert "three" not in i._attributes
i.__delattr__("two")
assert "two" not in i._attributes
base_ref: SetAttr = i
setattr(base_ref, "sub_attr", 5)
assert base_ref.sub_attr == 5
base_ref.sub_attr = 6
assert base_ref.sub_attr == 6
with assertRaises(AttributeError):
setattr(base_ref, "subclass_var", "c")
base_ref.new_attr = "new_attr"
assert base_ref.new_attr == "new_attr"
del base_ref.new_attr
assert "new_attr" not in base_ref._attributes
def test_setattr_nonnative() -> None:
i = SetAttrNonNative(99, {"one": 1})
assert i.class_var == "x"
assert i.regular_attr == 99
assert i.one == 1
assert i.two == None
assert i.const == 42
i = i
i.__setattr__("two", "2")
assert i.two == "2"
i.__setattr__("regular_attr", 101)
assert i.regular_attr == 101
with assertRaises(AttributeError):
i.__setattr__("class_var", "y")
with assertRaises(AttributeError):
i.__setattr__("const", 43)
setattr(i, "three", (3,3,3))
assert i.three == (3,3,3)
setattr(i, "regular_attr", 102)
assert i.regular_attr == 102
with assertRaises(AttributeError):
setattr(i, "class_var", "z")
with assertRaises(AttributeError):
setattr(i, "const", 44)
i.four = [4,4]
assert i.four == [4,4]
i.regular_attr = 103
assert i.regular_attr == 103
with assertRaises(AttributeError):
i.const = 45
# Doesn't work because there's no __delattr__.
with assertRaises(AttributeError):
del i.four
def test_no_setattr() -> None:
i = NoSetAttr(99)
i.super_setattr("attr", 100)
assert i.attr == 100
i.object_setattr("attr", 101)
assert i.attr == 101
object.__setattr__(i, "attr", 102)
assert i.attr == 102
with assertRaises(AttributeError):
i.super_setattr("not_attr", 100)
with assertRaises(AttributeError):
i.object_setattr("not_attr", 101)
with assertRaises(AttributeError):
object.__setattr__(i, "not_attr", 102)
def test_no_setattr_nonnative() -> None:
i = NoSetAttrNonNative(99)
i.super_setattr("attr", 100)
assert i.attr == 100
i.object_setattr("attr", 101)
assert i.attr == 101
object.__setattr__(i, "attr", 102)
assert i.attr == 102
i.super_setattr("one", 100)
assert i.one == 100
i.object_setattr("two", 101)
assert i.two == 101
object.__setattr__(i, "three", 102)
assert i.three == 102
i = i
del i.three
assert i.three == None
delattr(i, "two")
assert i.two == None
object.__delattr__(i, "one")
assert i.one == None
[typing fixtures/typing-full.pyi]
[case testDunderSetAttrInterpreted]
from mypy_extensions import mypyc_attr
from typing import ClassVar
class SetAttr:
_attributes: dict[str, object]
regular_attr: int
class_var: ClassVar[str] = "x"
const: int = 42
def __init__(self, regular_attr: int, extra_attrs: dict[str, object]) -> None:
super().__setattr__("_attributes", extra_attrs)
super().__setattr__("regular_attr", regular_attr)
def __setattr__(self, key: str, val: object) -> None:
if key == "regular_attr":
super().__setattr__("regular_attr", val)
elif key == "class_var" or key == "const":
raise AttributeError()
else:
self._attributes[key] = val
def __getattr__(self, key: str) -> object:
return self._attributes.get(key)
class SetAttrInherited(SetAttr):
def __init__(self, regular_attr: int, extra_attrs: dict[str, object]) -> None:
super().__init__(regular_attr, extra_attrs)
class SetAttrOverridden(SetAttr):
sub_attr: int
subclass_var: ClassVar[str] = "y"
def __init__(self, regular_attr: int, sub_attr: int, extra_attrs: dict[str, object]) -> None:
super().__init__(regular_attr, extra_attrs)
object.__setattr__(self, "sub_attr", sub_attr)
def __setattr__(self, key: str, val: object) -> None:
if key == "sub_attr":
object.__setattr__(self, "sub_attr", val)
elif key == "subclass_var":
raise AttributeError()
else:
super().__setattr__(key, val)
def __delattr__(self, key: str) -> None:
del self._attributes[key]
@mypyc_attr(native_class=False)
class SetAttrNonNative:
_attributes: dict[str, object]
regular_attr: int
class_var: ClassVar[str] = "x"
const: int = 42
def __init__(self, regular_attr: int, extra_attrs: dict[str, object]) -> None:
super().__setattr__("_attributes", extra_attrs)
super().__setattr__("regular_attr", regular_attr)
def __setattr__(self, key: str, val: object) -> None:
if key == "regular_attr":
super().__setattr__("regular_attr", val)
elif key == "class_var" or key == "const":
raise AttributeError()
else:
self._attributes[key] = val
def __getattr__(self, key: str) -> object:
return self._attributes.get(key)
class NoSetAttr:
def __init__(self, attr: int) -> None:
self.attr = attr
def object_setattr(self, attr: str, val: object) -> None:
object.__setattr__(self, attr, val)
def super_setattr(self, attr: str, val: object) -> None:
super().__setattr__(attr, val)
@mypyc_attr(native_class=False)
class NoSetAttrNonNative:
def __init__(self, attr: int) -> None:
self.attr = attr
def object_setattr(self, attr: str, val: object) -> None:
object.__setattr__(self, attr, val)
def super_setattr(self, attr: str, val: object) -> None:
super().__setattr__(attr, val)
def __getattr__(self, attr: str) -> object:
pass
[file driver.py]
from native import SetAttr, SetAttrInherited, SetAttrOverridden, SetAttrNonNative, NoSetAttr, NoSetAttrNonNative
from testutil import assertRaises
def test_setattr() -> None:
i = SetAttr(99, {"one": 1})
assert i.class_var == "x"
assert i.regular_attr == 99
assert i.one == 1
assert i.two == None
assert i.const == 42
i.__setattr__("two", "2")
assert i.two == "2"
i.__setattr__("regular_attr", 101)
assert i.regular_attr == 101
with assertRaises(AttributeError):
i.__setattr__("class_var", "y")
with assertRaises(AttributeError):
i.__setattr__("const", 43)
setattr(i, "three", (3,3,3))
assert i.three == (3,3,3)
setattr(i, "regular_attr", 102)
assert i.regular_attr == 102
with assertRaises(AttributeError):
setattr(i, "class_var", "z")
with assertRaises(AttributeError):
setattr(i, "const", 44)
i.four = [4,4]
assert i.four == [4,4]
i.regular_attr = 103
assert i.regular_attr == 103
with assertRaises(AttributeError):
i.const = 45
# Doesn't work because there's no __delattr__.
with assertRaises(AttributeError):
del i.four
def test_setattr_inherited() -> None:
i = SetAttrInherited(99, {"one": 1})
assert i.class_var == "x"
assert i.regular_attr == 99
assert i.one == 1
assert i.two == None
assert i.const == 42
i.__setattr__("two", "2")
assert i.two == "2"
i.__setattr__("regular_attr", 101)
assert i.regular_attr == 101
with assertRaises(AttributeError):
i.__setattr__("class_var", "y")
with assertRaises(AttributeError):
i.__setattr__("const", 43)
setattr(i, "three", (3,3,3))
assert i.three == (3,3,3)
setattr(i, "regular_attr", 102)
assert i.regular_attr == 102
with assertRaises(AttributeError):
setattr(i, "class_var", "z")
with assertRaises(AttributeError):
setattr(i, "const", 44)
i.four = [4,4]
assert i.four == [4,4]
i.regular_attr = 103
assert i.regular_attr == 103
with assertRaises(AttributeError):
i.const = 45
# Doesn't work because there's no __delattr__.
with assertRaises(AttributeError):
del i.four
def test_setattr_overridden() -> None:
i = SetAttrOverridden(99, 1, {"one": 1})
assert i.class_var == "x"
assert i.subclass_var == "y"
assert i.regular_attr == 99
assert i.sub_attr == 1
assert i.one == 1
assert i.two == None
assert i.const == 42
i.__setattr__("two", "2")
assert i.two == "2"
i.__setattr__("regular_attr", 101)
assert i.regular_attr == 101
i.__setattr__("sub_attr", 2)
assert i.sub_attr == 2
with assertRaises(AttributeError):
i.__setattr__("class_var", "y")
with assertRaises(AttributeError):
i.__setattr__("subclass_var", "a")
with assertRaises(AttributeError):
i.__setattr__("const", 43)
setattr(i, "three", (3,3,3))
assert i.three == (3,3,3)
setattr(i, "regular_attr", 102)
assert i.regular_attr == 102
setattr(i, "sub_attr", 3)
assert i.sub_attr == 3
with assertRaises(AttributeError):
setattr(i, "class_var", "z")
with assertRaises(AttributeError):
setattr(i, "subclass_var", "b")
with assertRaises(AttributeError):
setattr(i, "const", 44)
i.four = [4,4]
assert i.four == [4,4]
i.regular_attr = 103
assert i.regular_attr == 103
i.sub_attr = 4
assert i.sub_attr == 4
with assertRaises(AttributeError):
i.const = 45
del i.four
assert "four" not in i._attributes
delattr(i, "three")
assert "three" not in i._attributes
i.__delattr__("two")
assert "two" not in i._attributes
base_ref: SetAttr = i
setattr(base_ref, "sub_attr", 5)
assert base_ref.sub_attr == 5
base_ref.sub_attr = 6
assert base_ref.sub_attr == 6
with assertRaises(AttributeError):
setattr(base_ref, "subclass_var", "c")
base_ref.new_attr = "new_attr"
assert base_ref.new_attr == "new_attr"
del base_ref.new_attr
assert "new_attr" not in base_ref._attributes
def test_setattr_nonnative() -> None:
i = SetAttrNonNative(99, {"one": 1})
assert i.class_var == "x"
assert i.regular_attr == 99
assert i.one == 1
assert i.two == None
assert i.const == 42
i.__setattr__("two", "2")
assert i.two == "2"
i.__setattr__("regular_attr", 101)
assert i.regular_attr == 101
with assertRaises(AttributeError):
i.__setattr__("class_var", "y")
with assertRaises(AttributeError):
i.__setattr__("const", 43)
setattr(i, "three", (3,3,3))
assert i.three == (3,3,3)
setattr(i, "regular_attr", 102)
assert i.regular_attr == 102
with assertRaises(AttributeError):
setattr(i, "class_var", "z")
with assertRaises(AttributeError):
setattr(i, "const", 44)
i.four = [4,4]
assert i.four == [4,4]
i.regular_attr = 103
assert i.regular_attr == 103
with assertRaises(AttributeError):
i.const = 45
# Doesn't work because there's no __delattr__.
with assertRaises(AttributeError):
del i.four
def test_no_setattr() -> None:
i = NoSetAttr(99)
i.super_setattr("attr", 100)
assert i.attr == 100
i.object_setattr("attr", 101)
assert i.attr == 101
object.__setattr__(i, "attr", 102)
assert i.attr == 102
with assertRaises(AttributeError):
i.super_setattr("not_attr", 100)
with assertRaises(AttributeError):
i.object_setattr("not_attr", 101)
with assertRaises(AttributeError):
object.__setattr__(i, "not_attr", 102)
def test_no_setattr_nonnative() -> None:
i = NoSetAttrNonNative(99)
i.super_setattr("attr", 100)
assert i.attr == 100
i.object_setattr("attr", 101)
assert i.attr == 101
object.__setattr__(i, "attr", 102)
assert i.attr == 102
i.super_setattr("one", 100)
assert i.one == 100
i.object_setattr("two", 101)
assert i.two == 101
object.__setattr__(i, "three", 102)
assert i.three == 102
del i.three
assert i.three == None
delattr(i, "two")
assert i.two == None
object.__delattr__(i, "one")
assert i.one == None
test_setattr()
test_setattr_inherited()
test_setattr_overridden()
test_setattr_nonnative()
test_no_setattr()
test_no_setattr_nonnative()
[typing fixtures/typing-full.pyi]
[case testDunderSetAttrDefinedAfterAssignment]
setattr_calls: list[str] = []
def assign_in_function(obj: "LateSetAttr") -> None:
obj.attr = 1
class Other:
def assign_in_other_class(self, obj: "LateSetAttr") -> None:
obj.attr = 2
class LateSetAttr:
def __init__(self) -> None:
self.attr = 0
def assign_in_method(self) -> None:
self.attr = 3
self.attr += 1
def __setattr__(self, key: str, val: object) -> None:
setattr_calls.append(f"{key}={val}")
super().__setattr__(key, val)
class AssignInSetAttr:
attr: int
assigned: bool
def __init__(self) -> None:
object.__setattr__(self, "attr", 0)
object.__setattr__(self, "assigned", False)
def __setattr__(self, key: str, val: object) -> None:
setattr_calls.append(f"{key}={val}")
if key == "attr":
# This calls __setattr__ again.
self.assigned = True
super().__setattr__(key, val)
def test_setattr_defined_after_assignment() -> None:
setattr_calls.clear()
obj = LateSetAttr()
assert setattr_calls == ["attr=0"]
assign_in_function(obj)
assert setattr_calls == ["attr=0", "attr=1"]
Other().assign_in_other_class(obj)
assert setattr_calls == ["attr=0", "attr=1", "attr=2"]
obj.assign_in_method()
assert setattr_calls == ["attr=0", "attr=1", "attr=2", "attr=3", "attr=4"]
assert obj.attr == 4
def test_assignment_in_setattr() -> None:
setattr_calls.clear()
obj = AssignInSetAttr()
assert setattr_calls == []
obj.attr = 1
assert setattr_calls == ["attr=1", "assigned=True"]
assert obj.attr == 1
assert obj.assigned
[case testDelAttrWithDeletableAttr]
from testutil import assertRaises
class DelAttr:
__deletable__ = ["del_counter"]
_attributes: dict[str, object]
del_counter: int = 0
def __init__(self) -> None:
object.__setattr__(self, "_attributes", {})
def __setattr__(self, key: str, val: object) -> None:
if key == "del_counter":
object.__setattr__(self, "del_counter", val)
else:
self._attributes[key] = val
def __delattr__(self, key: str) -> None:
if key == "del_counter":
self.del_counter += 1
else:
del self._attributes[key]
def test_deletable_attr() -> None:
i = DelAttr()
assert i.del_counter == 0
del i.del_counter
assert i.del_counter == 1
[case testDelAttrWithDeletableAttrInterpreted]
class DelAttr:
__deletable__ = ["del_counter"]
_attributes: dict[str, object]
del_counter: int = 0
def __init__(self) -> None:
object.__setattr__(self, "_attributes", {})
def __setattr__(self, key: str, val: object) -> None:
if key == "del_counter":
object.__setattr__(self, "del_counter", val)
else:
self._attributes[key] = val
def __delattr__(self, key: str) -> None:
if key == "del_counter":
self.del_counter += 1
else:
del self._attributes[key]
[file driver.py]
from native import DelAttr
from testutil import assertRaises
def test_deletable_attr() -> None:
i = DelAttr()
assert i.del_counter == 0
del i.del_counter
assert i.del_counter == 1
test_deletable_attr()
[case testBufferCorruptedData_librt_internal]
from librt.internal import (
ReadBuffer, read_bool, read_str, read_float, read_int, read_tag, read_bytes
)
from random import randbytes
def check(data: bytes) -> None:
b = ReadBuffer(data)
try:
while True:
read_bool(b)
except ValueError:
pass
b = ReadBuffer(data)
read_tag(b) # Always succeeds
try:
while True:
read_int(b)
except ValueError:
pass
b = ReadBuffer(data)
try:
while True:
read_str(b)
except ValueError:
pass
b = ReadBuffer(data)
try:
while True:
read_bytes(b)
except ValueError:
pass
b = ReadBuffer(data)
try:
while True:
read_float(b)
except ValueError:
pass
import time
def test_read_corrupted_data() -> None:
# Test various deterministic byte sequences (1 to 4 bytes).
t0 = time.time()
for a in range(256):
check(bytes([a]))
for a in range(256):
for b in range(256):
check(bytes([a, b]))
for a in range(32):
for b in range(48):
for c in range(48):
check(bytes([a, b, c]))
for a in range(32):
for b in (0, 5, 17, 34):
for c in (0, 5, 17, 34):
for d in (0, 5, 17, 34):
check(bytes([a, b, c, d]))
# Also test some random data.
for i in range(20000):
data = randbytes(16)
try:
check(data)
except BaseException as e:
print("RANDOMIZED TEST FAILURE -- please open an issue with the following context:")
print(">>>", e, data)
raise
[case testAcyclicClass]
import gc
from mypy_extensions import mypyc_attr
@mypyc_attr(acyclic=True)
class Leaf:
def __init__(self, x: int, s: str) -> None:
self.x = x
self.s = s
def test_basic() -> None:
o = Leaf(5, "hello")
assert o.x == 5
assert o.s == "hello"
o.x = 10
assert o.x == 10
def test_gc_not_tracked() -> None:
o = Leaf(1, "a")
assert not gc.is_tracked(o)
def test_dealloc() -> None:
for i in range(1000):
o = Leaf(i, str(i))
# Just verify no crash or leak during repeated alloc/dealloc
@mypyc_attr(acyclic=True)
class AcyclicBase:
def __init__(self, x: int) -> None:
self.x = x
class DerivedNotAcyclic(AcyclicBase):
"""Derived without acyclic -- still participates in GC."""
def __init__(self, x: int, y: str) -> None:
super().__init__(x)
self.y = y
@mypyc_attr(acyclic=True)
class DerivedAcyclic(AcyclicBase):
"""Derived with acyclic -- also opts out of GC."""
def __init__(self, x: int, y: str) -> None:
super().__init__(x)
self.y = y
def test_derived_not_acyclic() -> None:
d = DerivedNotAcyclic(3, "hi")
assert d.x == 3
assert d.y == "hi"
# Subclass without @mypyc_attr(acyclic=True) still participates in GC
assert gc.is_tracked(d)
def test_derived_acyclic() -> None:
d = DerivedAcyclic(3, "hi")
assert d.x == 3
assert d.y == "hi"
assert not gc.is_tracked(d)
[case testTraitVtableGrandparentMethod]
from typing import Any
from mypy_extensions import trait
@trait
class Base:
def value(self) -> Any:
return 42
@trait
class Middle(Base):
pass
@trait
class Child(Middle):
def derived(self) -> Any:
return self.value()
class Concrete(Child):
pass
class Sub(Concrete):
def value(self) -> Any:
return 99
[file driver.py]
from native import Concrete, Sub
c = Concrete()
assert c.value() == 42
assert c.derived() == 42
s = Sub()
assert s.value() == 99
assert s.derived() == 99
[case testTraitVtableGrandparentMethodAllowInterpretedSubclasses]
from typing import Any
from mypy_extensions import trait, mypyc_attr
@mypyc_attr(allow_interpreted_subclasses=True)
@trait
class Base:
def value(self) -> Any:
return 42
@mypyc_attr(allow_interpreted_subclasses=True)
@trait
class Middle(Base):
pass
@mypyc_attr(allow_interpreted_subclasses=True)
@trait
class Child(Middle):
def derived(self) -> Any:
return self.value()
@mypyc_attr(allow_interpreted_subclasses=True)
class Concrete(Child):
pass
[file driver.py]
from native import Concrete
c = Concrete()
assert c.value() == 42
assert c.derived() == 42
[case testPropertyShadowVtableGlue]
from typing import List
import mypy_extensions
@mypy_extensions.mypyc_attr(allow_interpreted_subclasses=True)
class Base:
_x: str
_y: List[int]
def __init__(self) -> None:
self._x = ""
self._y = []
@property
def x(self) -> str:
return self._x
@x.setter
def x(self, v: str) -> None:
self._x = v
@property
def y(self) -> List[int]:
return self._y
@y.setter
def y(self, v: List[int]) -> None:
self._y = v
def method(self) -> str:
self.x = "a"
self.y = [1]
return self.x + str(len(self.y))
[file driver.py]
from native import Base
Sub = type("Sub", (Base,), {})
s = Sub()
assert s.method() == "a1"
[case testClassVarSelfReference]
# ClassVar initializers that reference other ClassVars from the same class.
# In CPython, the class body executes as a function where earlier assignments
# are available to later ones. mypyc must replicate this by loading from the
# class being built (type object for ext classes, class dict for non-ext)
# instead of module globals.
from typing import ClassVar, Dict, Set
class Ext:
A: ClassVar[Set[int]] = {1, 2, 3}
B: ClassVar[Set[int]] = {4, 5, 6}
C: ClassVar[Set[int]] = A | B
class ExtChained:
X: ClassVar[Set[int]] = {1, 2}
Y: ClassVar[Set[int]] = X | {3}
Z: ClassVar[Set[int]] = Y | {4}
class ExtDict:
BASE: ClassVar[Dict[str, int]] = {"a": 1, "b": 2}
EXTENDED: ClassVar[Dict[str, int]] = {**BASE, "c": 3}
class ExtSub(Ext):
E: ClassVar[Set[int]] = {7, 8}
[file driver.py]
from native import Ext, ExtChained, ExtDict, ExtSub
assert Ext.A == {1, 2, 3}
assert Ext.B == {4, 5, 6}
assert Ext.C == {1, 2, 3, 4, 5, 6}
assert ExtChained.X == {1, 2}
assert ExtChained.Y == {1, 2, 3}
assert ExtChained.Z == {1, 2, 3, 4}
assert ExtDict.BASE == {"a": 1, "b": 2}
assert ExtDict.EXTENDED == {"a": 1, "b": 2, "c": 3}
assert ExtSub.C == {1, 2, 3, 4, 5, 6}
assert ExtSub.E == {7, 8}
[case testClassVarSelfReferenceNonExt]
# Same as testClassVarSelfReference but for non-extension classes.
from typing import ClassVar, Dict, Set
from mypy_extensions import mypyc_attr
@mypyc_attr(native_class=False)
class NonExt:
A: ClassVar[Set[str]] = {"a", "b"}
B: ClassVar[Set[str]] = {"c"}
C: ClassVar[Set[str]] = A | B
@mypyc_attr(native_class=False)
class NonExtDict:
BASE: ClassVar[Dict[str, int]] = {"x": 1}
EXTENDED: ClassVar[Dict[str, int]] = {**BASE, "y": 2}
@mypyc_attr(native_class=False)
class NonExtChained:
X: ClassVar[Set[int]] = {10}
Y: ClassVar[Set[int]] = X | {20}
Z: ClassVar[Set[int]] = Y | {30}
[file driver.py]
from native import NonExt, NonExtDict, NonExtChained
assert NonExt.A == {"a", "b"}
assert NonExt.B == {"c"}
assert NonExt.C == {"a", "b", "c"}
assert NonExtDict.BASE == {"x": 1}
assert NonExtDict.EXTENDED == {"x": 1, "y": 2}
assert NonExtChained.Z == {10, 20, 30}
[case testPropertyGetterLeak]
class Bar:
pass
class Foo:
def __init__(self) -> None:
self.obj: object = Bar()
@property
def val(self) -> object:
return self.obj
[file other.py]
import gc
from native import Foo, Bar
def check(foo: Foo) -> bool:
return isinstance(foo.val, Bar)
def test_property_getter_no_leak() -> None:
gc.collect()
before = gc.get_objects()
for _ in range(100):
foo = Foo()
check(foo)
gc.collect()
after = gc.get_objects()
diff = len(after) - len(before)
assert diff <= 2, diff
test_property_getter_no_leak()
[file driver.py]
import sys
from other import check
from native import Foo
foo = Foo()
init = sys.getrefcount(foo.obj)
for _ in range(100):
check(foo)
after = sys.getrefcount(foo.obj)
assert after - init == 0, f"Leaked {after - init} refs"
[case testNativeAttrSetterRefcountLeak]
# Setting a native attribute from interpreted code goes through the generated
# getset descriptor setter. For refcounted unboxed types (heap-boxed ints,
# tuples with refcounted items) the setter must take exactly one reference to
# the stored value, not two.
from dataclasses import dataclass
from typing import Tuple
@dataclass
class IntField:
v: int
@dataclass
class TupleField:
v: Tuple[int, int]
[file driver.py]
import sys
from native import IntField, TupleField
# A heap-boxed int (>= 2**62) is stored as a refcounted PyObject*, unlike small
# inline-tagged ints, so an over-incref strands a real reference. Compute the
# values at runtime (not as folded literals): on free-threaded builds code
# constants are immortal, and getrefcount could not observe a leak on them.
shift = 70
BIG = 1 << shift
def check_no_leak(make, value) -> None:
base = sys.getrefcount(value)
objs = [make(value) for _ in range(100)]
alive = sys.getrefcount(value)
# Each live instance must hold exactly one reference to the field value.
assert alive - base == 100, f"expected 100 live refs, got {alive - base}"
del objs
after = sys.getrefcount(value)
assert after == base, f"leaked {after - base} refs"
# The dataclass-generated __init__ stores self.v = v via the descriptor setter.
check_no_leak(IntField, BIG)
# Tuple[int, int] is stored as an unboxed RTuple; its boxed-int elements are
# refcounted, so the setter must not over-incref them either. Use the same int
# in both slots so each instance holds exactly two references to it.
ELEM = 1 << (shift + 1)
base = sys.getrefcount(ELEM)
objs = [TupleField((ELEM, ELEM)) for _ in range(100)]
alive = sys.getrefcount(ELEM)
assert alive - base == 200, f"expected 200 live refs, got {alive - base}"
del objs
assert sys.getrefcount(ELEM) == base, f"tuple field leaked {sys.getrefcount(ELEM) - base} refs"
# Re-assigning through the setter must release the previous value too.
o = IntField(BIG)
base = sys.getrefcount(BIG)
o.v = BIG
o.v = BIG
assert sys.getrefcount(BIG) == base, "reassignment leaked refs"
[case testNativeAttrSetterTypeErrorPreservesValue]
import sys
from typing import Any
from testutil import assertRaises
class Fields:
def __init__(self, items: list[int], number: int, pair: tuple[int, int]) -> None:
self.items = items
self.number = number
self.pair = pair
class DeletableFields:
__deletable__ = ("pair",)
def __init__(self, pair: tuple[int, int]) -> None:
self.pair = pair
def test_type_error_preserves_value() -> None:
getrefcount: Any = getattr(sys, "getrefcount")
items = [1]
shift = 70
number = 1 << shift
pair_number = 1 << (shift + 1)
pair = (pair_number, pair_number)
fields = Fields(items, number, pair)
dynamic_fields: Any = fields
items_refcount = getrefcount(items)
number_refcount = getrefcount(number)
pair_number_refcount = getrefcount(pair_number)
with assertRaises(AttributeError):
del dynamic_fields.items
# Do not make successful pointer assignments before these checks. Free-threaded
# builds reclaim the replaced value using a QSBR-delayed decref.
with assertRaises(TypeError):
dynamic_fields.items = object()
with assertRaises(TypeError):
dynamic_fields.number = object()
with assertRaises(TypeError):
dynamic_fields.pair = object()
assert getrefcount(items) == items_refcount
assert getrefcount(number) == number_refcount
assert getrefcount(pair_number) == pair_number_refcount
assert fields.items is items
assert fields.number is number
assert fields.pair == pair
assert fields.pair[0] is pair_number
def test_deletion_releases_value() -> None:
getrefcount: Any = getattr(sys, "getrefcount")
shift = 70
number = 1 << shift
# Single-word reference fields use delayed decrefs on free-threaded builds. An
# unboxed tuple instead exercises the synchronous deletion path changed by the fix.
pair = (number, number)
fields = DeletableFields(pair)
dynamic_fields: Any = fields
stored_refcount = getrefcount(number)
del dynamic_fields.pair
after = getrefcount(number)
assert after == stored_refcount - 2, (stored_refcount, after)
# Keep the compiled tuple local live across both refcount measurements.
assert pair[0] is number
with assertRaises(AttributeError):
fields.pair
[case testBorrowedFinalAttributeInLambdaAndNestedFunction]
from typing import Final, Callable
class Box:
def __init__(self, n: int) -> None:
self.n = n
class C:
def __init__(self, b: Box) -> None:
self.b: Final = b
def use(x: Box, y: Box) -> int:
return x.n + y.n
def spend(x: Box) -> Box:
# Allocate a lot so a freed Box slot is likely to be reused, turning any
# use-after-free of a borrowed attribute into an observable wrong result.
junk = [Box(i) for i in range(100)]
return Box(x.n + junk[50].n - 50)
def make_lambda() -> Callable[[], int]:
# The lambda body creates a C, borrows its final attribute, then calls
# spend() (which allocates heavily) before reading the borrow.
return lambda: use(C(Box(111)).b, spend(Box(0)))
def make_nested() -> Callable[[], int]:
def inner() -> int:
return use(C(Box(111)).b, spend(Box(0)))
return inner
def test_borrowed_final_attribute_in_lambda() -> None:
f = make_lambda()
for _ in range(1000):
assert f() == 111
def test_borrowed_final_attribute_in_nested_function() -> None:
g = make_nested()
for _ in range(1000):
assert g() == 111
[case testBorrowedFinalAttributeInComprehension]
from typing import Final
class Box:
def __init__(self, n: int) -> None:
self.n = n
class C:
def __init__(self, b: Box) -> None:
self.b: Final = b
def use(x: Box, y: Box) -> int:
return x.n + y.n
def spend(x: Box) -> Box:
# Allocate a lot so a freed Box slot is likely to be reused, turning any
# use-after-free of a borrowed attribute into an observable wrong result.
junk = [Box(i) for i in range(100)]
return Box(x.n + junk[50].n - 50)
def comp(ns: list[int]) -> list[int]:
# Each iteration allocates a fresh C, borrows its final attribute .b, then
# calls spend() (which allocates heavily) before reading the borrow. The
# borrow container must be kept alive for the current iteration only; if it
# is scoped to the whole (comprehension) expression, the refcount pass
# dec_refs an uninitialized temporary on the loop back-edge and leaks/uses
# freed memory.
return [use(C(Box(111)).b, spend(Box(i))) for i in ns]
def test_borrowed_final_attribute_in_comprehension() -> None:
for _ in range(1000):
assert comp([1, 2, 3, 4, 5]) == [112, 113, 114, 115, 116]
[case testPropertyException]
from mypy_extensions import i16
from testutil import assertRaises
class T:
def __init__(self, val: int) -> None:
self._val = val
self._val_overlaps: i16 = -1
self._tuple_val = (0, 0)
self._locked = 0
@property
def val(self) -> int:
return self._val
@val.setter
def val(self, x: int) -> None:
if x < 0:
raise ValueError("No")
self._val = x
@property
def val_overlaps(self) -> i16:
return self._val_overlaps
@val_overlaps.setter
def val_overlaps(self, x: i16) -> i16:
if x > 0:
raise ValueError("Invalid")
self._val_overlaps = x
return self._val_overlaps
@property
def tuple_val(self) -> tuple[int, int]:
return self._tuple_val
@tuple_val.setter
def tuple_val(self, x: tuple[int, int]) -> tuple[int, int]:
if x[0] < 0:
raise ValueError("Invalid tuple")
self._tuple_val = x
return self._tuple_val
@property
def locked(self) -> int:
raise ValueError("Locked")
def test_property_setter_exception() -> None:
t = T(1)
assert t.val == 1
assert t.val_overlaps == -1
t.val = 2
assert t.val == 2
t.val_overlaps = -113
assert t.val_overlaps == -113
t.val_overlaps = -1
assert t.val_overlaps == -1
t.tuple_val = (1, 2)
assert t.tuple_val == (1, 2)
setattr(t, "val_overlaps", -113)
assert t.val_overlaps == -113
setattr(t, "tuple_val", (3, 4))
assert t.tuple_val == (3, 4)
with assertRaises(ValueError):
t.val = -1
with assertRaises(ValueError):
t.val_overlaps = 1
with assertRaises(ValueError):
t.tuple_val = (-1, 0)
# Generic setattr goes through the Python descriptor setter instead of
# calling the native property setter directly through the vtable.
with assertRaises(ValueError):
setattr(t, "val", -1)
with assertRaises(ValueError):
setattr(t, "val_overlaps", 1)
with assertRaises(ValueError):
setattr(t, "tuple_val", (-1, 0))
def test_property_getter_exception() -> None:
t = T(1)
with assertRaises(ValueError):
print(t.locked)
with assertRaises(ValueError):
getattr(t, "locked")
[case testClassBodyFinalAttributeNoInstanceSlot]
from typing import Any, Final, final
from testutil import assertRaises
calls: list[int] = []
def make() -> int:
calls.append(1)
return 3
class C:
LITERAL: Final = 23
COMPUTED: Final = make()
ITEMS: Final = [1, 2]
def literal(self) -> int:
return self.LITERAL
def computed(self) -> int:
return self.COMPUTED
def items(self) -> list[int]:
return self.ITEMS
class Sub(C):
def inherited(self) -> int:
return self.LITERAL + self.COMPUTED
@final
class Sealed:
X: Final = 5
def get(self) -> int:
return self.X
def test_native_read() -> None:
assert C().literal() == 23
assert C().computed() == 3
assert C().items() == [1, 2]
assert Sealed().get() == 5
def test_inherited_read() -> None:
assert Sub().inherited() == 26
assert Sub().literal() == 23
assert Sub.LITERAL == 23
assert Sub().LITERAL == 23
def test_initializer_evaluated_once() -> None:
# The class body runs once, so a side-effecting initializer must not be
# re-evaluated for each instance.
C()
C()
assert len(calls) == 1
def test_no_instance_slot() -> None:
# Class-body Finals are not instance state, so they are absent from
# __mypyc_attrs__ (which drives pickling).
t: Any = C
assert 'LITERAL' not in t.__mypyc_attrs__
assert 'COMPUTED' not in t.__mypyc_attrs__
assert 'ITEMS' not in t.__mypyc_attrs__
def test_dynamic_read_via_any() -> None:
o: Any = C()
assert o.LITERAL == 23
assert o.COMPUTED == 3
assert o.ITEMS == [1, 2]
assert getattr(o, 'LITERAL') == 23
s: Any = Sub()
assert s.LITERAL == 23
assert s.COMPUTED == 3
def test_dynamic_read_through_class() -> None:
t: Any = C
assert t.LITERAL == 23
assert t.COMPUTED == 3
assert t.ITEMS == [1, 2]
assert getattr(t, 'COMPUTED') == 3
def test_dynamic_read_matches_native() -> None:
o: Any = C()
assert o.ITEMS is C().items()
assert o.COMPUTED == C().computed()
def test_cannot_rebind_via_any() -> None:
o: Any = C()
with assertRaises(AttributeError):
o.LITERAL = 1
assert o.LITERAL == 23
with assertRaises(AttributeError):
setattr(o, 'COMPUTED', 1)
assert o.COMPUTED == 3
[case testClassBodyFinalAttributeInTrait]
from mypy_extensions import trait
from typing import Any, Final
def make() -> int:
return 7
@trait
class T:
LITERAL: Final = 23
COMPUTED: Final = make()
def literal(self) -> int:
return self.LITERAL
class C(T):
def computed(self) -> int:
return self.COMPUTED
def test_trait_final() -> None:
assert C().literal() == 23
assert C().computed() == 7
assert T.LITERAL == 23
assert C.COMPUTED == 7
def test_trait_final_via_any() -> None:
o: Any = C()
assert o.LITERAL == 23
assert o.COMPUTED == 7
[case testClassBodyFinalAttributeOfNativeIntType]
from mypy_extensions import i64
from typing import Any, Final
def make() -> i64:
return -113
class C:
# Native int types overlap with the error sentinel used by Final statics, so
# these keep their instance slot.
N: Final = make()
def get(self) -> i64:
return self.N
def test_native_int_final() -> None:
assert C().get() == -113
o: Any = C()
assert o.N == -113
[case testClassBodyFinalAttributeThroughUnionAndAny]
from typing import Any, Final, Union
class C:
X: Final = 23
class D:
X: Final = 24
class E1(C):
pass
class E2(C):
pass
def through_union_same_decl(x: Union[E1, E2]) -> int:
# Two sibling subclasses, so neither item is the declaring class itself, but both
# inherit the same declaration and can use C's value.
return x.X
def through_union_different_decls(x: Union[C, D]) -> int:
# The value differs per item, so this must read the attribute per object.
return x.X
def through_any(x: Any) -> Any:
return x.X
def through_narrowing(x: object) -> int:
assert isinstance(x, C)
return x.X
def test_union_same_decl() -> None:
assert through_union_same_decl(E1()) == 23
assert through_union_same_decl(E2()) == 23
def test_union_different_decls() -> None:
assert through_union_different_decls(C()) == 23
assert through_union_different_decls(D()) == 24
def test_any() -> None:
assert through_any(C()) == 23
assert through_any(D()) == 24
assert through_any(E1()) == 23
assert through_any(C) == 23
def test_narrowing() -> None:
assert through_narrowing(C()) == 23
assert through_narrowing(E1()) == 23
[case testClassBodyFinalAttributeWithInterpretedSubclasses]
from mypy_extensions import mypyc_attr
from typing import Any, Final
@mypyc_attr(allow_interpreted_subclasses=True)
class C:
plain: int = 7
LITERAL: Final = 5
ITEMS: Final = [0] * 100
def __init__(self) -> None:
self.inst = 1
def read_plain(self) -> int:
return self.plain
def read_inst(self) -> int:
return self.inst
def read_literal(self) -> int:
return self.LITERAL
def read_items(self) -> list[int]:
return self.ITEMS
def make_shadowing_subclass() -> Any:
type2: Any = type
return type2('Sub', (C,), {'plain': 99, 'inst': 98, 'LITERAL': 97, 'ITEMS': [1]})
def test_no_instance_slot() -> None:
t: Any = C
assert 'plain' in t.__mypyc_attrs__
assert 'inst' in t.__mypyc_attrs__
assert 'LITERAL' not in t.__mypyc_attrs__
assert 'ITEMS' not in t.__mypyc_attrs__
def test_initializer_evaluated_once() -> None:
# Not re-evaluated per instance, and native and interpreted reads agree.
assert C().read_items() is C().read_items()
assert C().read_items() is C.ITEMS
assert C().read_items() is C().ITEMS
def test_interpreted_subclass_shadowing() -> None:
# A class-dict entry in an interpreted subclass shadows only interpreted reads.
# This matches how plain and __init__-assigned attributes already behave.
o = make_shadowing_subclass()()
assert (o.read_plain(), o.plain) == (7, 99)
assert (o.read_inst(), o.inst) == (1, 98)
assert (o.read_literal(), o.LITERAL) == (5, 97)
assert o.read_items() is C.ITEMS
assert o.ITEMS == [1]