cpython/py_class/
mod.rs

1// Copyright (c) 2016 Daniel Grunwald
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19pub mod gc;
20
21#[doc(hidden)]
22pub mod members;
23
24#[allow(clippy::module_inception)]
25mod py_class;
26
27#[cfg(feature = "python27-sys")]
28#[rustfmt::skip]
29mod py_class_impl2;
30
31#[cfg(feature = "python3-sys")]
32#[rustfmt::skip]
33mod py_class_impl3;
34
35#[doc(hidden)]
36pub mod slots;
37
38use std::{cell, mem, ptr};
39
40use crate::err::{self, PyResult};
41use crate::ffi;
42use crate::objects::{PyModule, PyObject, PyType};
43use crate::python::{self, Python, PythonObject};
44
45// TODO: consider moving CompareOp to a different module, so that it isn't exported via two paths
46#[derive(Debug)]
47pub enum CompareOp {
48    Lt = ffi::Py_LT as isize,
49    Le = ffi::Py_LE as isize,
50    Eq = ffi::Py_EQ as isize,
51    Ne = ffi::Py_NE as isize,
52    Gt = ffi::Py_GT as isize,
53    Ge = ffi::Py_GE as isize,
54}
55
56/// Trait implemented by the types produced by the `py_class!()` macro.
57///
58/// This is an unstable implementation detail; do not implement manually!
59pub trait PythonObjectFromPyClassMacro: python::PythonObjectWithTypeObject {
60    /// Initializes the class.
61    ///
62    /// module_name: the name of the parent module into which the class will be placed.
63    fn initialize(py: Python, module_name: Option<&str>) -> PyResult<PyType>;
64
65    /// Initializes the class and adds it to the module.
66    fn add_to_module(py: Python, module: &PyModule) -> PyResult<()>;
67}
68
69#[inline]
70#[doc(hidden)]
71pub fn data_offset<T>(base_size: usize) -> usize {
72    let align = mem::align_of::<T>();
73    // round base_size up to next multiple of align
74    (base_size + align - 1) / align * align
75}
76
77#[inline]
78#[doc(hidden)]
79pub fn data_new_size<T>(base_size: usize) -> usize {
80    data_offset::<T>(base_size) + mem::size_of::<T>()
81}
82
83#[inline]
84#[doc(hidden)]
85pub unsafe fn data_get<'a, T>(_py: Python<'a>, obj: &'a PyObject, offset: usize) -> &'a T {
86    let ptr = (obj.as_ptr() as *const u8).add(offset) as *const T;
87    &*ptr
88}
89
90#[inline]
91#[doc(hidden)]
92pub unsafe fn data_init<'a, T>(_py: Python<'a>, obj: &'a PyObject, offset: usize, value: T)
93where
94    T: Send + 'static,
95{
96    let ptr = (obj.as_ptr() as *mut u8).add(offset) as *mut T;
97    ptr::write(ptr, value)
98}
99
100#[inline]
101#[doc(hidden)]
102pub unsafe fn data_drop<T>(_py: Python<'_>, obj: *mut ffi::PyObject, offset: usize) {
103    let ptr = (obj as *mut u8).add(offset) as *mut T;
104    ptr::drop_in_place(ptr)
105}
106
107#[inline]
108#[doc(hidden)]
109pub fn is_ready(_py: Python, ty: &ffi::PyTypeObject) -> bool {
110    (ty.tp_flags & ffi::Py_TPFLAGS_READY) != 0
111}
112
113/// A PythonObject that is usable as a base type with the `py_class!()` macro.
114pub trait BaseObject: PythonObject {
115    /// Gets the size of the object, in bytes.
116    fn size() -> usize;
117
118    type InitType;
119
120    /// Allocates a new object (usually by calling ty->tp_alloc),
121    /// and initializes it using init_val.
122    /// `ty` must be derived from the Self type, and the resulting object
123    /// must be of type `ty`.
124    unsafe fn alloc(py: Python, ty: &PyType, init_val: Self::InitType) -> PyResult<PyObject>;
125
126    /// Calls the rust destructor for the object and frees the memory
127    /// (usually by calling ptr->ob_type->tp_free).
128    /// This function is used as tp_dealloc implementation.
129    unsafe fn dealloc(py: Python, obj: *mut ffi::PyObject);
130}
131
132impl BaseObject for PyObject {
133    #[inline]
134    fn size() -> usize {
135        mem::size_of::<ffi::PyObject>()
136    }
137
138    type InitType = ();
139
140    unsafe fn alloc(py: Python, ty: &PyType, _init_val: ()) -> PyResult<PyObject> {
141        let ptr = ffi::PyType_GenericAlloc(ty.as_type_ptr(), 0);
142        //println!("BaseObject::alloc({:?}) = {:?}", ty.as_type_ptr(), ptr);
143        err::result_from_owned_ptr(py, ptr)
144    }
145
146    unsafe fn dealloc(_py: Python, obj: *mut ffi::PyObject) {
147        //println!("BaseObject::dealloc({:?})", ptr);
148        // Unfortunately, there is no PyType_GenericFree, so
149        // we have to manually un-do the work of PyType_GenericAlloc:
150        let ty = ffi::Py_TYPE(obj);
151        if ffi::PyType_IS_GC(ty) != 0 {
152            ffi::PyObject_GC_Del(obj as *mut libc::c_void);
153        } else {
154            ffi::PyObject_Free(obj as *mut libc::c_void);
155        }
156        // For heap types, PyType_GenericAlloc calls INCREF on the type objects,
157        // so we need to call DECREF here:
158        if ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 {
159            ffi::Py_DECREF(ty as *mut ffi::PyObject);
160        }
161    }
162}