| [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] |