Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add __pyo3__ attribute to native modules #3449

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions newsfragments/3449.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add '__pyo3__' attribue to native modules
18 changes: 17 additions & 1 deletion src/impl_/pymodule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ use std::{
sync::atomic::{self, AtomicBool},
};

use crate::{exceptions::PyImportError, ffi, types::PyModule, Py, PyResult, Python};
use crate::{
exceptions::PyImportError,
ffi,
types::{PyModule, PyO3Attr},
Py, PyResult, Python,
};

/// `Sync` wrapper of `ffi::PyModuleDef`.
pub struct ModuleDef {
Expand Down Expand Up @@ -74,6 +79,7 @@ impl ModuleDef {
let module = unsafe {
Py::<PyModule>::from_owned_ptr_or_err(py, ffi::PyModule_Create(self.ffi_def.get()))?
};
module.setattr(py, "__pyo3__", PyO3Attr {})?;
if self.initialized.swap(true, atomic::Ordering::SeqCst) {
return Err(PyImportError::new_err(
"PyO3 modules may only be initialized once per interpreter process",
Expand Down Expand Up @@ -122,6 +128,16 @@ mod tests {
.unwrap(),
"some doc",
);
assert_eq!(
module
.getattr("__pyo3__")
.unwrap()
.getattr("version")
.unwrap()
.extract::<&str>()
.unwrap(),
env!("CARGO_PKG_VERSION")
);
assert_eq!(
module
.getattr("SOME_CONSTANT")
Expand Down
2 changes: 2 additions & 0 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub use self::none::PyNone;
pub use self::notimplemented::PyNotImplemented;
pub use self::num::PyLong;
pub use self::num::PyLong as PyInt;
pub(crate) use self::pyo3_attr::PyO3Attr;
#[cfg(not(PyPy))]
pub use self::pysuper::PySuper;
pub use self::sequence::PySequence;
Expand Down Expand Up @@ -293,6 +294,7 @@ mod module;
mod none;
mod notimplemented;
mod num;
pub(crate) mod pyo3_attr;
#[cfg(not(PyPy))]
mod pysuper;
mod sequence;
Expand Down
132 changes: 132 additions & 0 deletions src/types/pyo3_attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use std::ffi::CStr;

use crate::class::PyMethodDefType;
use crate::ffi::{reprfunc, PyTypeObject, PyType_Slot, Py_tp_repr};
use crate::impl_::{
extract_argument::{extract_pyclass_ref, PyFunctionArgument},
pycell::PyClassMutability,
pyclass::{
LazyTypeObject, PyClassBaseType, PyClassDummySlot, PyClassImpl, PyClassImplCollector,
PyClassItems, PyClassItemsIter, PyMethods, SendablePyClass,
},
trampoline::reprfunc,
};
use crate::intern;
use crate::methods::PyClassAttributeFactory;
use crate::pyclass::boolean_struct;
use crate::{IntoPy, Py, PyAny, PyCell, PyClass, PyObject, PyRef, PyResult, PyTypeInfo, Python};

pub(crate) struct PyO3Attr {}

unsafe impl PyTypeInfo for PyO3Attr {
type AsRefTarget = PyCell<Self>;
const NAME: &'static str = "_pyo3_internal.PyO3Attr";
const MODULE: Option<&'static str> = None;

#[inline]
fn type_object_raw(py: Python<'_>) -> *mut PyTypeObject {
<Self as PyClassImpl>::lazy_type_object()
.get_or_init(py)
.as_type_ptr()
}
}

impl PyClass for PyO3Attr {
type Frozen = boolean_struct::True;
}

impl<'a, 'py> PyFunctionArgument<'a, 'py> for &'a PyO3Attr {
type Holder = Option<PyRef<'py, PyO3Attr>>;

#[inline]
fn extract(obj: &'py PyAny, holder: &'a mut Self::Holder) -> PyResult<Self> {
extract_pyclass_ref(obj, holder)
}
}

impl IntoPy<PyObject> for PyO3Attr {
fn into_py(self, py: Python<'_>) -> PyObject {
IntoPy::into_py(Py::new(py, self).unwrap(), py)
}
}

impl PyClassImpl for PyO3Attr {
const IS_BASETYPE: bool = true;
type BaseType = PyAny;
type ThreadChecker = SendablePyClass<PyO3Attr>;
type PyClassMutability =
<<Self::BaseType as PyClassBaseType>::PyClassMutability as PyClassMutability>::ImmutableChild;
type Dict = PyClassDummySlot;
type WeakRef = PyClassDummySlot;
type BaseNativeType = Self::BaseType;

fn items_iter() -> PyClassItemsIter {
let collector = PyClassImplCollector::<Self>::new();
static INTRINSIC_ITEMS: PyClassItems = PyClassItems {
methods: &[PyMethodDefType::ClassAttribute(
crate::PyClassAttributeDef::new(
"version\0",
PyClassAttributeFactory(PyO3Attr::version),
),
)],
slots: &[{
unsafe extern "C" fn trampoline(
_slf: *mut crate::ffi::PyObject,
) -> *mut crate::ffi::PyObject {
reprfunc(_slf, PyO3Attr::__repr__)
}
PyType_Slot {
slot: Py_tp_repr,
pfunc: trampoline as reprfunc as _,
}
}],
};
PyClassItemsIter::new(&INTRINSIC_ITEMS, collector.py_methods())
}

fn doc(_py: Python<'_>) -> PyResult<&'static CStr> {
Ok(Self::DOC)
}

fn lazy_type_object() -> &'static LazyTypeObject<Self> {
static TYPE_OBJECT: LazyTypeObject<PyO3Attr> = LazyTypeObject::new();
&TYPE_OBJECT
}
}

impl PyO3Attr {
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const DOC: &'static CStr =
unsafe { CStr::from_bytes_with_nul_unchecked(b"The PyO3 information for a module.\0") };

fn version(py: Python<'_>) -> PyResult<PyObject> {
Ok(intern!(py, PyO3Attr::VERSION).into_py(py))
}

unsafe fn __repr__(
py: Python<'_>,
_raw_slf: *mut crate::ffi::PyObject,
) -> PyResult<*mut crate::ffi::PyObject> {
crate::callback::convert(py, intern!(py, <PyO3Attr as PyTypeInfo>::NAME))
}
}

#[cfg(test)]
mod test {
use super::PyO3Attr;
use crate::{IntoPy, Python};

#[test]
fn test_version_str() {
Python::with_gil(|py| {
let attr = IntoPy::into_py(PyO3Attr {}, py);
assert_eq!(
attr.getattr(py, "version")
.unwrap()
.extract::<&str>(py)
.unwrap(),
env!("CARGO_PKG_VERSION")
)
})
}
}