diff --git a/mypyc/primitives/weakref_ops.py b/mypyc/primitives/weakref_ops.py index a7ac035b22a4..21379d3b2c82 100644 --- a/mypyc/primitives/weakref_ops.py +++ b/mypyc/primitives/weakref_ops.py @@ -20,3 +20,21 @@ c_function_name="PyWeakref_NewRef", error_kind=ERR_MAGIC, ) + +new_proxy_op = function_op( + name="_weakref.proxy", + arg_types=[object_rprimitive], + return_type=object_rprimitive, + c_function_name="PyWeakref_NewProxy", + extra_int_constants=[(0, pointer_rprimitive)], + error_kind=ERR_MAGIC, +) + +new_proxy_with_callback_op = function_op( + name="_weakref.proxy", + arg_types=[object_rprimitive, object_rprimitive], + # steals=[True, False], + return_type=object_rprimitive, + c_function_name="PyWeakref_NewProxy", + error_kind=ERR_MAGIC, +) diff --git a/mypyc/test-data/fixtures/test-weakref.pyi b/mypyc/test-data/fixtures/test-weakref.pyi new file mode 100644 index 000000000000..73896835114b --- /dev/null +++ b/mypyc/test-data/fixtures/test-weakref.pyi @@ -0,0 +1,10 @@ +class dict: ... +class ellipsis: ... +class int: ... +class str: ... +class tuple: ... +class type: ... +class list: ... +class BaseException: ... +class Exception(BaseException): ... +class ReferenceError(Exception): ... diff --git a/mypyc/test-data/irbuild-weakref.test b/mypyc/test-data/irbuild-weakref.test index 58ac6417d297..2180b1e747aa 100644 --- a/mypyc/test-data/irbuild-weakref.test +++ b/mypyc/test-data/irbuild-weakref.test @@ -49,3 +49,55 @@ def f(x, cb): L0: r0 = PyWeakref_NewRef(x, cb) return r0 + +[case testWeakrefProxy] +import weakref +from typing import Any, Callable +def f(x: object) -> object: + return weakref.proxy(x) + +[out] +def f(x): + x, r0 :: object +L0: + r0 = PyWeakref_NewProxy(x, 0) + return r0 + +[case testWeakrefProxyCallback] +import weakref +from typing import Any, Callable +def f(x: object, cb: Callable[[object], Any]) -> object: + return weakref.proxy(x, cb) + +[out] +def f(x, cb): + x, cb, r0 :: object +L0: + r0 = PyWeakref_NewProxy(x, cb) + return r0 + +[case testFromWeakrefProxy] +from typing import Any, Callable +from weakref import proxy +def f(x: object) -> object: + return proxy(x) + +[out] +def f(x): + x, r0 :: object +L0: + r0 = PyWeakref_NewProxy(x, 0) + return r0 + +[case testFromWeakrefProxyCallback] +from typing import Any, Callable +from weakref import proxy +def f(x: object, cb: Callable[[object], Any]) -> object: + return proxy(x, cb) + +[out] +def f(x, cb): + x, cb, r0 :: object +L0: + r0 = PyWeakref_NewProxy(x, cb) + return r0 diff --git a/mypyc/test-data/run-weakref.test b/mypyc/test-data/run-weakref.test index 902c9e407ff4..0b070b1567fb 100644 --- a/mypyc/test-data/run-weakref.test +++ b/mypyc/test-data/run-weakref.test @@ -7,7 +7,6 @@ from mypy_extensions import mypyc_attr @mypyc_attr(native_class=False) class Object: """some random weakreffable object""" - pass def test_weakref_ref(): obj = Object() @@ -16,6 +15,20 @@ def test_weakref_ref(): obj = None assert r() is None, r() +[file driver.py] +from native import test_weakref_ref + +test_weakref_ref() + + +[case testWeakrefRefWithCallback] +from weakref import ref +from mypy_extensions import mypyc_attr + +@mypyc_attr(native_class=False) +class Object: + """some random weakreffable object""" + def test_weakref_ref_with_callback(): obj = Object() r = ref(obj, lambda x: x) @@ -24,7 +37,62 @@ def test_weakref_ref_with_callback(): assert r() is None, r() [file driver.py] -from native import test_weakref_ref, test_weakref_ref_with_callback +from native import test_weakref_ref_with_callback -test_weakref_ref() test_weakref_ref_with_callback() + + +[case testWeakrefProxy] +import pytest # type: ignore [import-not-found] +from weakref import proxy +from mypy_extensions import mypyc_attr + +@mypyc_attr(native_class=False) +class Object: + """some random weakreffable object""" + def some_meth(self) -> int: + return 1 + +def test_weakref_proxy(): + obj = Object() + p = proxy(obj) + assert obj.some_meth() == 1 + assert p.some_meth() == 1 + obj.some_meth() + obj = None + with pytest.raises(ReferenceError): + p.some_meth() + +[builtins fixtures/test-weakref.pyi] +[file driver.py] +from native import test_weakref_proxy + +test_weakref_proxy() + + +[case testWeakrefProxyWithCallback] +import pytest # type: ignore [import-not-found] +from weakref import proxy +from mypy_extensions import mypyc_attr + +@mypyc_attr(native_class=False) +class Object: + """some random weakreffable object""" + def some_meth(self) -> int: + return 1 + +def test_weakref_proxy_with_callback(): + obj = Object() + p = proxy(obj, lambda x: x) + assert obj.some_meth() == 1 + assert p.some_meth() == 1 + obj.some_meth() + obj = None + with pytest.raises(ReferenceError): + p.some_meth() + +[builtins fixtures/test-weakref.pyi] +[file driver.py] +from native import test_weakref_proxy_with_callback + +test_weakref_proxy_with_callback() diff --git a/test-data/unit/lib-stub/_weakref.pyi b/test-data/unit/lib-stub/_weakref.pyi new file mode 100644 index 000000000000..70012375e645 --- /dev/null +++ b/test-data/unit/lib-stub/_weakref.pyi @@ -0,0 +1,11 @@ +from typing import Any, Callable, TypeVar, overload +from weakref import CallableProxyType + +_C = TypeVar("_C", bound=Callable[..., Any]) +_T = TypeVar("_T") + +# Return CallableProxyType if object is callable, ProxyType otherwise +@overload +def proxy(object: _C, callback: Callable[[_C], Any] | None = None, /) -> CallableProxyType[_C]: ... +@overload +def proxy(object: _T, callback: Callable[[_T], Any] | None = None, /) -> Any: ... diff --git a/test-data/unit/lib-stub/weakref.pyi b/test-data/unit/lib-stub/weakref.pyi index 34e01f4d48f1..f26a52f351a3 100644 --- a/test-data/unit/lib-stub/weakref.pyi +++ b/test-data/unit/lib-stub/weakref.pyi @@ -1,7 +1,9 @@ +from _weakref import proxy from collections.abc import Callable -from typing import Any, Generic, TypeVar +from typing import Any, ClassVar, Generic, TypeVar, final from typing_extensions import Self +_C = TypeVar("_C", bound=Callable[..., Any]) _T = TypeVar("_T") class ReferenceType(Generic[_T]): # "weakref" @@ -9,3 +11,12 @@ class ReferenceType(Generic[_T]): # "weakref" def __new__(cls, o: _T, callback: Callable[[Self], Any] | None = ..., /) -> Self: ... ref = ReferenceType + +@final +class CallableProxyType(Generic[_C]): # "weakcallableproxy" + def __eq__(self, value: object, /) -> bool: ... + def __getattr__(self, attr: str) -> Any: ... + __call__: _C + __hash__: ClassVar[None] # type: ignore[assignment] + +__all__ = ["proxy"]