1use {
6 super::python_resource::ResourceCollectionContext,
7 python_packaging::{
8 resource::{PythonModuleSource, PythonResource},
9 resource_collection::PythonResourceAddCollectionContext,
10 },
11 starlark::values::{
12 error::{RuntimeError, UnsupportedOperation, ValueError},
13 {Mutable, TypedValue, Value, ValueResult},
14 },
15 std::sync::{Arc, Mutex, MutexGuard},
16};
17
18#[derive(Debug)]
19pub struct PythonModuleSourceWrapper {
20 pub m: PythonModuleSource,
21 pub add_context: Option<PythonResourceAddCollectionContext>,
22}
23
24#[derive(Debug, Clone)]
26pub struct PythonModuleSourceValue {
27 inner: Arc<Mutex<PythonModuleSourceWrapper>>,
28 name: String,
29}
30
31impl PythonModuleSourceValue {
32 pub fn new(module: PythonModuleSource) -> Self {
33 let name = module.name.clone();
34
35 Self {
36 inner: Arc::new(Mutex::new(PythonModuleSourceWrapper {
37 m: module,
38 add_context: None,
39 })),
40 name,
41 }
42 }
43
44 pub fn inner(&self, label: &str) -> Result<MutexGuard<PythonModuleSourceWrapper>, ValueError> {
45 self.inner.try_lock().map_err(|e| {
46 ValueError::Runtime(RuntimeError {
47 code: "PYTHON_MODULE_SOURCE",
48 message: format!("failed to acquire lock: {}", e),
49 label: label.to_string(),
50 })
51 })
52 }
53}
54
55impl ResourceCollectionContext for PythonModuleSourceValue {
56 fn add_collection_context(
57 &self,
58 ) -> Result<Option<PythonResourceAddCollectionContext>, ValueError> {
59 Ok(self
60 .inner("PythonModuleSource.add_collection_context()")?
61 .add_context
62 .clone())
63 }
64
65 fn replace_add_collection_context(
66 &mut self,
67 context: PythonResourceAddCollectionContext,
68 ) -> Result<Option<PythonResourceAddCollectionContext>, ValueError> {
69 Ok(self
70 .inner("PythonModuleSource.replace_add_collection_context()")?
71 .add_context
72 .replace(context))
73 }
74
75 fn as_python_resource(&self) -> Result<PythonResource<'_>, ValueError> {
76 Ok(PythonResource::from(
77 self.inner("PythonModuleSource.as_python_resource()")?
78 .m
79 .clone(),
80 ))
81 }
82}
83
84impl TypedValue for PythonModuleSourceValue {
85 type Holder = Mutable<PythonModuleSourceValue>;
86 const TYPE: &'static str = "PythonModuleSource";
87
88 fn values_for_descendant_check_and_freeze(&self) -> Box<dyn Iterator<Item = Value>> {
89 Box::new(std::iter::empty())
90 }
91
92 fn to_str(&self) -> String {
93 format!("{}<name={}>", Self::TYPE, self.name)
94 }
95
96 fn to_repr(&self) -> String {
97 self.to_str()
98 }
99
100 fn get_attr(&self, attribute: &str) -> ValueResult {
101 let inner = self.inner(&format!("PythonModuleSource.{}", attribute))?;
102
103 let v = match attribute {
104 "is_stdlib" => Value::from(inner.m.is_stdlib),
105 "name" => Value::new(inner.m.name.clone()),
106 "source" => {
107 let source = inner.m.source.resolve_content().map_err(|e| {
108 ValueError::from(RuntimeError {
109 code: "PYOXIDIZER_SOURCE_ERROR",
110 message: format!("error resolving source code: {}", e),
111 label: "source".to_string(),
112 })
113 })?;
114
115 let source = String::from_utf8(source).map_err(|_| {
116 ValueError::from(RuntimeError {
117 code: "PYOXIDIZER_SOURCE_ERROR",
118 message: "error converting source code to UTF-8".to_string(),
119 label: "source".to_string(),
120 })
121 })?;
122
123 Value::new(source)
124 }
125 "is_package" => Value::new(inner.m.is_package),
126 attr => {
127 drop(inner);
128
129 return if self.add_collection_context_attrs().contains(&attr) {
130 self.get_attr_add_collection_context(attr)
131 } else {
132 Err(ValueError::OperationNotSupported {
133 op: UnsupportedOperation::GetAttr(attr.to_string()),
134 left: Self::TYPE.to_string(),
135 right: None,
136 })
137 };
138 }
139 };
140
141 Ok(v)
142 }
143
144 fn has_attr(&self, attribute: &str) -> Result<bool, ValueError> {
145 Ok(match attribute {
146 "name" => true,
147 "source" => true,
148 "is_package" => true,
149 "is_stdlib" => true,
150 attr => self.add_collection_context_attrs().contains(&attr),
151 })
152 }
153
154 fn set_attr(&mut self, attribute: &str, value: Value) -> Result<(), ValueError> {
155 if self.add_collection_context_attrs().contains(&attribute) {
156 self.set_attr_add_collection_context(attribute, value)
157 } else {
158 Err(ValueError::OperationNotSupported {
159 op: UnsupportedOperation::SetAttr(attribute.to_string()),
160 left: Self::TYPE.to_owned(),
161 right: None,
162 })
163 }
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use {
170 super::*,
171 crate::starlark::{python_distribution::PythonDistributionValue, testutil::*},
172 anyhow::Result,
173 starlark::values::none::NoneType,
174 };
175
176 #[test]
177 fn test_source_module_attrs() -> Result<()> {
178 let mut env = test_evaluation_context_builder()?.into_context()?;
179 add_exe(&mut env)?;
180
181 let dist_value = env.eval("dist")?;
182 let dist_ref = dist_value
183 .downcast_ref::<PythonDistributionValue>()
184 .unwrap();
185 let dist = dist_ref.distribution.as_ref().unwrap();
186
187 let mut m = env.eval("exe.make_python_module_source('foo', 'import bar')")?;
188
189 assert_eq!(m.get_type(), PythonModuleSourceValue::TYPE);
190 assert!(m.has_attr("name").unwrap());
191 assert_eq!(m.get_attr("name").unwrap().to_str(), "foo");
192
193 assert!(m.has_attr("source").unwrap());
194 assert_eq!(m.get_attr("source").unwrap().to_str(), "import bar");
195
196 assert!(m.has_attr("is_package").unwrap());
197 assert!(!m.get_attr("is_package").unwrap().to_bool());
198
199 assert!(m.has_attr("add_include").unwrap());
200 assert_eq!(m.get_attr("add_include").unwrap().get_type(), "bool");
201 assert!(m.get_attr("add_include").unwrap().to_bool());
202 m.set_attr("add_include", Value::new(false)).unwrap();
203 assert!(!m.get_attr("add_include").unwrap().to_bool());
204
205 assert!(m.has_attr("add_location").unwrap());
206 assert_eq!(m.get_attr("add_location").unwrap().to_str(), "in-memory");
207
208 m.set_attr("add_location", Value::from("in-memory"))
209 .unwrap();
210 assert_eq!(m.get_attr("add_location").unwrap().to_str(), "in-memory");
211
212 m.set_attr("add_location", Value::from("filesystem-relative:lib"))
213 .unwrap();
214 assert_eq!(
215 m.get_attr("add_location").unwrap().to_str(),
216 "filesystem-relative:lib"
217 );
218
219 assert!(m.has_attr("add_location_fallback").unwrap());
220
221 assert_eq!(
222 m.get_attr("add_location_fallback").unwrap().get_type(),
223 if dist.supports_in_memory_shared_library_loading() {
224 "string"
225 } else {
226 "NoneType"
227 }
228 );
229
230 m.set_attr("add_location_fallback", Value::from("in-memory"))
231 .unwrap();
232 assert_eq!(
233 m.get_attr("add_location_fallback").unwrap().to_str(),
234 "in-memory"
235 );
236
237 m.set_attr(
238 "add_location_fallback",
239 Value::from("filesystem-relative:lib"),
240 )
241 .unwrap();
242 assert_eq!(
243 m.get_attr("add_location_fallback").unwrap().to_str(),
244 "filesystem-relative:lib"
245 );
246
247 m.set_attr("add_location_fallback", Value::from(NoneType::None))
248 .unwrap();
249 assert_eq!(
250 m.get_attr("add_location_fallback").unwrap().get_type(),
251 "NoneType"
252 );
253
254 assert!(m.has_attr("add_source").unwrap());
255 assert_eq!(m.get_attr("add_source").unwrap().get_type(), "bool");
256 assert!(m.get_attr("add_source").unwrap().to_bool());
257 m.set_attr("add_source", Value::new(false)).unwrap();
258 assert!(!m.get_attr("add_source").unwrap().to_bool());
259
260 assert!(m.has_attr("add_bytecode_optimization_level_zero").unwrap());
261 assert_eq!(
262 m.get_attr("add_bytecode_optimization_level_zero")
263 .unwrap()
264 .get_type(),
265 "bool"
266 );
267 assert!(m
268 .get_attr("add_bytecode_optimization_level_zero")
269 .unwrap()
270 .to_bool(),);
271 m.set_attr("add_bytecode_optimization_level_zero", Value::new(false))
272 .unwrap();
273 assert!(!m
274 .get_attr("add_bytecode_optimization_level_zero")
275 .unwrap()
276 .to_bool());
277
278 assert!(m.has_attr("add_bytecode_optimization_level_one").unwrap());
279 assert_eq!(
280 m.get_attr("add_bytecode_optimization_level_one")
281 .unwrap()
282 .get_type(),
283 "bool"
284 );
285 assert!(!m
286 .get_attr("add_bytecode_optimization_level_one")
287 .unwrap()
288 .to_bool());
289 m.set_attr("add_bytecode_optimization_level_one", Value::new(true))
290 .unwrap();
291 assert!(m
292 .get_attr("add_bytecode_optimization_level_one")
293 .unwrap()
294 .to_bool(),);
295
296 assert!(m.has_attr("add_bytecode_optimization_level_two").unwrap());
297 assert_eq!(
298 m.get_attr("add_bytecode_optimization_level_two")
299 .unwrap()
300 .get_type(),
301 "bool"
302 );
303 assert!(!m
304 .get_attr("add_bytecode_optimization_level_two")
305 .unwrap()
306 .to_bool());
307 m.set_attr("add_bytecode_optimization_level_two", Value::new(true))
308 .unwrap();
309 assert!(m
310 .get_attr("add_bytecode_optimization_level_two")
311 .unwrap()
312 .to_bool());
313
314 Ok(())
315 }
316}