diff --git a/src/fmt.rs b/src/fmt.rs index 2b5554182f1..5482e6bbeca 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -42,7 +42,13 @@ macro_rules! py_format { static INTERNED: $crate::sync::PyOnceLock<$crate::Py<$crate::types::PyString>> = $crate::sync::PyOnceLock::new(); Ok( INTERNED - .get_or_init($py, || $crate::types::PyString::intern($py, static_string).unbind()) + .get_or_init($py, || { + if let Ok(static_c_string) = ::std::ffi::CString::new(static_string) { + $crate::types::PyString::intern_cstr($py, &static_c_string).unbind() + } else { + $crate::types::PyString::intern($py, static_string).unbind() + } + }) .bind($py) .to_owned() ) diff --git a/src/impl_/coroutine.rs b/src/impl_/coroutine.rs index 0bb2d341a17..d47a8d6b053 100644 --- a/src/impl_/coroutine.rs +++ b/src/impl_/coroutine.rs @@ -2,13 +2,12 @@ use std::future::Future; use crate::{ coroutine::{cancel::ThrowCallback, Coroutine}, - instance::Bound, types::PyString, - Py, PyAny, PyResult, Python, + Borrowed, Py, PyAny, PyResult, Python, }; pub fn new_coroutine<'py, F>( - name: &Bound<'py, PyString>, + name: Borrowed<'_, 'py, PyString>, qualname_prefix: Option<&'static str>, throw_callback: Option, future: F, @@ -16,7 +15,12 @@ pub fn new_coroutine<'py, F>( where F: Future>> + Send + 'static, { - Coroutine::new(Some(name.clone()), qualname_prefix, throw_callback, future) + Coroutine::new( + Some(name.to_owned()), + qualname_prefix, + throw_callback, + future, + ) } /// Handle which assumes that the coroutine is attached to the thread. Unlike `Python<'_>`, this is `Send`. diff --git a/src/sync.rs b/src/sync.rs index cb71ccd10f5..fe081c3bc15 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -9,12 +9,7 @@ //! interpreter. //! //! This module provides synchronization primitives which are able to synchronize under these conditions. -use crate::{ - internal::state::SuspendAttach, - sealed::Sealed, - types::{PyAny, PyString}, - Bound, Py, Python, -}; +use crate::{internal::state::SuspendAttach, sealed::Sealed, types::PyAny, Bound, Python}; use std::{ cell::UnsafeCell, marker::PhantomData, @@ -229,30 +224,27 @@ impl Drop for GILOnceCell { #[macro_export] macro_rules! intern { ($py: expr, $text: expr) => {{ - static INTERNED: $crate::sync::Interned = $crate::sync::Interned::new($text); - INTERNED.get($py) + const STRING: ::std::result::Result<&::std::ffi::CStr, &str> = { + match ::std::ffi::CStr::from_bytes_with_nul(concat!($text, "\0").as_bytes()) { + ::std::result::Result::Ok(c_str) => ::std::result::Result::Ok(c_str), + ::std::result::Result::Err(_) => ::std::result::Result::Err($text), + } + }; + static INTERNED: $crate::sync::PyOnceLock<$crate::Py<$crate::types::PyString>> = + $crate::sync::PyOnceLock::new(); + INTERNED + .get_or_init($py, || match STRING { + ::std::result::Result::Ok(c_str) => { + $crate::types::PyString::intern_cstr($py, c_str).unbind() + } + ::std::result::Result::Err(string) => { + $crate::types::PyString::intern($py, string).unbind() + } + }) + .bind_borrowed($py) }}; } -/// Implementation detail for `intern!` macro. -#[doc(hidden)] -pub struct Interned(&'static str, PyOnceLock>); - -impl Interned { - /// Creates an empty holder for an interned `str`. - pub const fn new(value: &'static str) -> Self { - Interned(value, PyOnceLock::new()) - } - - /// Gets or creates the interned `str` value. - #[inline] - pub fn get<'py>(&self, py: Python<'py>) -> &Bound<'py, PyString> { - self.1 - .get_or_init(py, || PyString::intern(py, self.0).into()) - .bind(py) - } -} - /// Extension trait for [`Once`] to help avoid deadlocking when using a [`Once`] when attached to a /// Python thread. pub trait OnceExt: Sealed { @@ -727,6 +719,8 @@ mod tests { use super::*; use crate::types::{PyAnyMethods, PyDict, PyDictMethods}; + #[cfg(feature = "macros")] + use crate::Py; #[cfg(not(target_arch = "wasm32"))] #[cfg(feature = "macros")] use std::sync::atomic::{AtomicBool, Ordering}; diff --git a/src/types/module.rs b/src/types/module.rs index 795ec737eeb..639b79cd869 100644 --- a/src/types/module.rs +++ b/src/types/module.rs @@ -551,11 +551,11 @@ impl<'py> PyModuleMethods<'py> for Bound<'py, PyModule> { } } -fn __all__(py: Python<'_>) -> &Bound<'_, PyString> { +fn __all__(py: Python<'_>) -> Borrowed<'static, '_, PyString> { intern!(py, "__all__") } -fn __name__(py: Python<'_>) -> &Bound<'_, PyString> { +fn __name__(py: Python<'_>) -> Borrowed<'static, '_, PyString> { intern!(py, "__name__") } diff --git a/src/types/string.rs b/src/types/string.rs index a2010d3f434..84a04ab56a6 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -202,6 +202,18 @@ impl PyString { } } + /// Intern the given C string. + /// + /// Python may keep a reference to the returned string or make it immortal, preventing it from + /// being garbage collected. + pub fn intern_cstr<'py>(py: Python<'py>, s: &CStr) -> Bound<'py, PyString> { + unsafe { + ffi::PyUnicode_InternFromString(s.as_ptr()) + .assume_owned(py) + .cast_into_unchecked() + } + } + /// Attempts to create a Python string from a Python [bytes-like object]. /// /// The `encoding` and `errors` parameters are optional: