numcodecs_python/
codec_class.rs

1use pyo3::{
2    ffi::PyTypeObject,
3    intern,
4    prelude::*,
5    types::{DerefToPyAny, PyDict, PyType},
6    PyTypeInfo,
7};
8
9use crate::{sealed::Sealed, PyCodec};
10
11/// Represents a [`numcodecs.abc.Codec`] *class* object.
12///
13/// The [`Bound<CodecClass>`] type implements the [`PyCodecClassMethods`] API.
14///
15/// [`numcodecs.abc.Codec`]: https://numcodecs.readthedocs.io/en/stable/abc.html#module-numcodecs.abc
16#[repr(transparent)]
17pub struct PyCodecClass {
18    _class: PyType,
19}
20
21/// Methods implemented for [`PyCodecClass`]es.
22pub trait PyCodecClassMethods<'py>: Sealed {
23    /// Gets the codec identifier.
24    ///
25    /// # Errors
26    ///
27    /// Errors if the codec does not provide an identifier.
28    fn codec_id(&self) -> Result<String, PyErr>;
29
30    /// Instantiate a codec from a configuration dictionary.
31    ///
32    /// The `config` dict must *not* contain an `id` field.
33    ///
34    /// # Errors
35    ///
36    /// Errors if constructing the codec fails.
37    fn codec_from_config(
38        &self,
39        config: Borrowed<'_, 'py, PyDict>,
40    ) -> Result<Bound<'py, PyCodec>, PyErr>;
41
42    /// Gets the [`PyType`] that this [`PyCodecClass`] represents.
43    fn as_type(&self) -> &Bound<'py, PyType>;
44}
45
46impl<'py> PyCodecClassMethods<'py> for Bound<'py, PyCodecClass> {
47    fn codec_id(&self) -> Result<String, PyErr> {
48        let py = self.py();
49
50        let codec_id = self.as_any().getattr(intern!(py, "codec_id"))?.extract()?;
51
52        Ok(codec_id)
53    }
54
55    fn codec_from_config(
56        &self,
57        config: Borrowed<'_, 'py, PyDict>,
58    ) -> Result<Bound<'py, PyCodec>, PyErr> {
59        let py = self.py();
60
61        self.as_any()
62            .call_method1(intern!(py, "from_config"), (config,))?
63            .extract()
64    }
65
66    fn as_type(&self) -> &Bound<'py, PyType> {
67        #[expect(unsafe_code)]
68        // Safety: PyCodecClass is a wrapper around PyType
69        unsafe {
70            self.downcast_unchecked()
71        }
72    }
73}
74
75impl Sealed for Bound<'_, PyCodecClass> {}
76
77#[doc(hidden)]
78impl DerefToPyAny for PyCodecClass {}
79
80#[doc(hidden)]
81#[expect(unsafe_code)]
82unsafe impl PyTypeInfo for PyCodecClass {
83    const MODULE: Option<&'static str> = Some("numcodecs.abc");
84    const NAME: &'static str = "Codec";
85
86    #[inline]
87    fn type_object_raw(py: Python) -> *mut PyTypeObject {
88        PyType::type_object_raw(py)
89    }
90
91    #[inline]
92    fn is_type_of(object: &Bound<'_, PyAny>) -> bool {
93        let Ok(ty) = object.downcast::<PyType>() else {
94            return false;
95        };
96
97        ty.is_subclass_of::<PyCodec>().unwrap_or(false)
98    }
99
100    #[inline]
101    fn is_exact_type_of(object: &Bound<'_, PyAny>) -> bool {
102        object.as_ptr() == PyCodec::type_object_raw(object.py()).cast()
103    }
104}