Skip to content

Commit 8f94d51

Browse files
committed
implement PyTypeInfo on PyWeakref and PyWeakrefProxy
Create a new ABC class and make the relevant classes virtual subclasses of it
1 parent 0588240 commit 8f94d51

File tree

3 files changed

+72
-28
lines changed

3 files changed

+72
-28
lines changed

newsfragments/5401.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Implement `PyTypeInfo` on `PyWeakref` and `PyWeakrefProxy` using dynamically generated ABC classes

src/types/weakref/anyref.rs

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,42 @@
11
use crate::err::PyResult;
22
use crate::ffi_ptr_ext::FfiPtrExt;
3+
use crate::sync::PyOnceLock;
34
use crate::type_object::{PyTypeCheck, PyTypeInfo};
45
use crate::types::any::PyAny;
5-
use crate::{ffi, Bound};
6+
use crate::types::{PyAnyMethods, PyDict, PyType, PyTypeMethods};
7+
use crate::{ffi, Bound, Py};
68

79
/// Represents any Python `weakref` reference.
810
///
911
/// In Python this is created by calling `weakref.ref` or `weakref.proxy`.
1012
#[repr(transparent)]
1113
pub struct PyWeakref(PyAny);
1214

13-
pyobject_native_type_named!(PyWeakref);
15+
pyobject_native_type_core!(
16+
PyWeakref,
17+
|py| {
18+
static TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
19+
TYPE.get_or_try_init(py, || {
20+
// Terrible way to generate a super class of both ProxyType and CallableProxyType
21+
let globals = PyDict::new(py);
22+
globals.set_item("abc", py.import("abc")?)?;
23+
py.run(ffi::c_str!("class Weakref(abc.ABC):\n pass"), Some(&globals), None)?;
24+
let cls = globals.get_item("Weakref")?.downcast_into::<PyType>()?;
25+
let weakref = py.import("weakref")?;
26+
for child_name in ["ProxyType", "CallableProxyType", "ReferenceType"] {
27+
cls.call_method1("register", (weakref.getattr(child_name)?,))?;
28+
}
29+
PyResult::Ok(cls.unbind())
30+
}).unwrap().bind(py).as_type_ptr()
31+
},
32+
#module=Some("weakref"),
33+
#checkfunction=ffi::PyWeakref_Check
34+
);
1435

1536
// TODO: We known the layout but this cannot be implemented, due to the lack of public typeobject pointers
1637
// #[cfg(not(Py_LIMITED_API))]
1738
// pyobject_native_type_sized!(PyWeakref, ffi::PyWeakReference);
1839

19-
impl PyTypeCheck for PyWeakref {
20-
const NAME: &'static str = "weakref";
21-
#[cfg(feature = "experimental-inspect")]
22-
const PYTHON_TYPE: &'static str = "weakref.ProxyTypes";
23-
24-
fn type_check(object: &Bound<'_, PyAny>) -> bool {
25-
unsafe { ffi::PyWeakref_Check(object.as_ptr()) > 0 }
26-
}
27-
}
28-
2940
/// Implementation of functionality for [`PyWeakref`].
3041
///
3142
/// These methods are defined for the `Bound<'py, PyWeakref>` smart pointer, so to use method call

src/types/weakref/proxy.rs

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::err::PyResult;
22
use crate::ffi_ptr_ext::FfiPtrExt;
33
use crate::py_result_ext::PyResultExt;
4-
use crate::type_object::PyTypeCheck;
4+
use crate::sync::PyOnceLock;
55
use crate::types::any::PyAny;
6-
use crate::{ffi, Borrowed, Bound, BoundObject, IntoPyObject, IntoPyObjectExt};
7-
8-
use super::PyWeakrefMethods;
6+
use crate::types::weakref::PyWeakrefMethods;
7+
use crate::types::{PyAnyMethods, PyDict, PyType, PyTypeMethods};
8+
use crate::{ffi, Borrowed, Bound, BoundObject, IntoPyObject, IntoPyObjectExt, Py};
99

1010
/// Represents any Python `weakref` Proxy type.
1111
///
@@ -14,22 +14,31 @@ use super::PyWeakrefMethods;
1414
#[repr(transparent)]
1515
pub struct PyWeakrefProxy(PyAny);
1616

17-
pyobject_native_type_named!(PyWeakrefProxy);
17+
pyobject_native_type_core!(
18+
PyWeakrefProxy,
19+
|py| {
20+
static TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
21+
TYPE.get_or_try_init(py, || {
22+
// Terrible way to generate a super class of both ProxyType and CallableProxyType
23+
let globals = PyDict::new(py);
24+
globals.set_item("abc", py.import("abc")?)?;
25+
py.run(ffi::c_str!("class WeakrefProxy(abc.ABC):\n pass"), Some(&globals), None)?;
26+
let cls = globals.get_item("WeakrefProxy")?.downcast_into::<PyType>()?;
27+
let weakref = py.import("weakref")?;
28+
for child_name in ["ProxyType", "CallableProxyType"] {
29+
cls.call_method1("register", (weakref.getattr(child_name)?,))?;
30+
}
31+
PyResult::Ok(cls.unbind())
32+
}).unwrap().bind(py).as_type_ptr()
33+
},
34+
#module=Some("weakref"),
35+
#checkfunction=ffi::PyWeakref_CheckProxy
36+
);
1837

1938
// TODO: We known the layout but this cannot be implemented, due to the lack of public typeobject pointers. And it is 2 distinct types
2039
// #[cfg(not(Py_LIMITED_API))]
2140
// pyobject_native_type_sized!(PyWeakrefProxy, ffi::PyWeakReference);
2241

23-
impl PyTypeCheck for PyWeakrefProxy {
24-
const NAME: &'static str = "weakref.ProxyTypes";
25-
#[cfg(feature = "experimental-inspect")]
26-
const PYTHON_TYPE: &'static str = "weakref.ProxyType | weakref.CallableProxyType";
27-
28-
fn type_check(object: &Bound<'_, PyAny>) -> bool {
29-
unsafe { ffi::PyWeakref_CheckProxy(object.as_ptr()) > 0 }
30-
}
31-
}
32-
3342
/// TODO: UPDATE DOCS
3443
impl PyWeakrefProxy {
3544
/// Constructs a new Weak Reference (`weakref.proxy`/`weakref.ProxyType`/`weakref.CallableProxyType`) for the given object.
@@ -183,7 +192,7 @@ mod tests {
183192
use crate::exceptions::{PyAttributeError, PyReferenceError, PyTypeError};
184193
use crate::types::any::{PyAny, PyAnyMethods};
185194
use crate::types::weakref::{PyWeakrefMethods, PyWeakrefProxy};
186-
use crate::{Bound, PyResult, Python};
195+
use crate::{Bound, PyResult, PyTypeInfo, Python};
187196

188197
#[cfg(all(Py_3_13, not(Py_LIMITED_API)))]
189198
const DEADREF_FIX: Option<&str> = None;
@@ -412,6 +421,18 @@ mod tests {
412421
Ok(())
413422
})
414423
}
424+
425+
#[test]
426+
fn test_type_object() -> PyResult<()> {
427+
Python::attach(|py| {
428+
let t = PyWeakrefProxy::type_object(py);
429+
let class = get_type(py)?;
430+
let object = class.call0()?;
431+
let reference = PyWeakrefProxy::new(&object)?;
432+
assert!(reference.is_instance(&t)?);
433+
Ok(())
434+
})
435+
}
415436
}
416437

417438
// under 'abi3-py37' and 'abi3-py38' PyClass cannot be weakreferencable.
@@ -718,6 +739,17 @@ mod tests {
718739
Ok(())
719740
})
720741
}
742+
743+
#[test]
744+
fn test_type_object() -> PyResult<()> {
745+
Python::attach(|py| {
746+
let class = get_type(py)?;
747+
let object = class.call0()?;
748+
let reference = PyWeakrefProxy::new(&object)?;
749+
assert!(reference.is_instance(&PyWeakrefProxy::type_object(py))?);
750+
Ok(())
751+
})
752+
}
721753
}
722754

723755
// under 'abi3-py37' and 'abi3-py38' PyClass cannot be weakreferencable.

0 commit comments

Comments
 (0)