|
1 | 1 | use super::{ |
2 | | - PyGenericAlias, PyStr, PyType, PyTypeRef, |
| 2 | + PyDictRef, PyGenericAlias, PyStr, PyType, PyTypeRef, |
| 3 | + builtin_func::PyNativeFunction, |
3 | 4 | classmethod::{ |
4 | 5 | descriptor_get_wrapped_attribute, descriptor_set_wrapped_attribute, functools_wraps, |
5 | 6 | }, |
| 7 | + type_::{PyAttributes, rendered_item_doc}, |
6 | 8 | }; |
7 | 9 | use crate::{ |
8 | 10 | AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, |
9 | 11 | class::{PyClassDef, PyClassImpl}, |
| 12 | + common::{hash::HashSecret, lock::PyMutex, wtf8::Wtf8Buf}, |
10 | 13 | function::{FuncArgs, PySetterValue}, |
11 | 14 | object::PyAtomicRef, |
12 | 15 | types::{Callable, Constructor, GetDescriptor, Initializer, Representable}, |
13 | 16 | }; |
14 | 17 |
|
| 18 | +// Context::genesis builds native descriptors before the first interpreter |
| 19 | +// chooses its hash seed. Delay hashing their metadata until that seed exists. |
| 20 | +rustpython_common::static_cell! { |
| 21 | + static NATIVE_METADATA: PyMutex<NativeMetadata>; |
| 22 | +} |
| 23 | + |
| 24 | +#[derive(Default)] |
| 25 | +struct NativeMetadata { |
| 26 | + hash_secret: Option<&'static HashSecret>, |
| 27 | + pending: Vec<(PyDictRef, PyAttributes)>, |
| 28 | +} |
| 29 | + |
15 | 30 | #[pyclass(module = false, name = "staticmethod", traverse)] |
16 | 31 | #[derive(Debug)] |
17 | 32 | pub struct PyStaticMethod { |
@@ -75,6 +90,58 @@ impl Constructor for PyStaticMethod { |
75 | 90 | } |
76 | 91 |
|
77 | 92 | impl PyStaticMethod { |
| 93 | + pub(crate) fn new_native( |
| 94 | + callable: PyRef<PyNativeFunction>, |
| 95 | + class: &Py<PyType>, |
| 96 | + ctx: &Context, |
| 97 | + ) -> PyRef<Self> { |
| 98 | + let mut qualname = class.heaptype_ext.as_ref().map_or_else( |
| 99 | + || Wtf8Buf::from_string(class.name().to_string()), |
| 100 | + |ext| ext.qualname.read().as_wtf8().to_owned(), |
| 101 | + ); |
| 102 | + qualname.push_str("."); |
| 103 | + qualname.push_str(callable.value.name); |
| 104 | + let attrs = [ |
| 105 | + ( |
| 106 | + identifier!(ctx, __module__), |
| 107 | + callable.module.load_owned().unwrap_or_else(|| ctx.none()), |
| 108 | + ), |
| 109 | + ( |
| 110 | + identifier!(ctx, __name__), |
| 111 | + ctx.new_str(callable.value.name).into(), |
| 112 | + ), |
| 113 | + (identifier!(ctx, __qualname__), ctx.new_str(qualname).into()), |
| 114 | + ( |
| 115 | + identifier!(ctx, __doc__), |
| 116 | + rendered_item_doc(callable.value.name, callable.value.item_doc()) |
| 117 | + .map_or_else(|| ctx.none(), |doc| ctx.new_str(doc).into()), |
| 118 | + ), |
| 119 | + ] |
| 120 | + .into_iter() |
| 121 | + .collect(); |
| 122 | + let dict = ctx.new_dict(); |
| 123 | + let wrapper = PyRef::new_ref( |
| 124 | + Self::new(callable.into()), |
| 125 | + ctx.types.staticmethod_type.to_owned(), |
| 126 | + Some(dict.clone()), |
| 127 | + ); |
| 128 | + let mut metadata = NATIVE_METADATA.get_or_init(PyMutex::default).lock(); |
| 129 | + if let Some(secret) = metadata.hash_secret { |
| 130 | + dict.entries.initialize_attributes(attrs, secret); |
| 131 | + } else { |
| 132 | + metadata.pending.push((dict, attrs)); |
| 133 | + } |
| 134 | + wrapper |
| 135 | + } |
| 136 | + |
| 137 | + pub(crate) fn init_native_metadata(secret: &'static HashSecret) { |
| 138 | + let mut metadata = NATIVE_METADATA.get_or_init(PyMutex::default).lock(); |
| 139 | + metadata.hash_secret = Some(secret); |
| 140 | + for (dict, attrs) in metadata.pending.drain(..) { |
| 141 | + dict.entries.initialize_attributes(attrs, secret); |
| 142 | + } |
| 143 | + } |
| 144 | + |
78 | 145 | #[must_use] |
79 | 146 | pub fn new(callable: PyObjectRef) -> Self { |
80 | 147 | Self { |
|
0 commit comments