1use {
6 super::python_resource::ResourceCollectionContext,
7 linked_hash_map::LinkedHashMap,
8 python_packaging::{
9 location::ConcreteResourceLocation,
10 policy::{ExtensionModuleFilter, PythonPackagingPolicy, ResourceHandlingMode},
11 },
12 starlark::{
13 environment::TypeValues,
14 eval::call_stack::CallStack,
15 starlark_fun, starlark_module, starlark_parse_param_type, starlark_signature,
16 starlark_signature_extraction, starlark_signatures,
17 values::{
18 error::{RuntimeError, UnsupportedOperation, ValueError},
19 none::NoneType,
20 Mutable, TypedValue, Value, ValueResult,
21 },
22 },
23 starlark_dialect_build_targets::required_type_arg,
24 std::{
25 ops::Deref,
26 sync::{Arc, Mutex, MutexGuard},
27 },
28};
29
30#[derive(Debug, Clone)]
31pub struct PythonPackagingPolicyValue {
32 inner: Arc<Mutex<PythonPackagingPolicy>>,
33
34 derive_context_callbacks: Vec<Value>,
36}
37
38impl PythonPackagingPolicyValue {
39 pub fn new(inner: PythonPackagingPolicy) -> Self {
40 Self {
41 inner: Arc::new(Mutex::new(inner)),
42 derive_context_callbacks: vec![],
43 }
44 }
45
46 pub fn inner(&self, label: &str) -> Result<MutexGuard<PythonPackagingPolicy>, ValueError> {
47 self.inner.try_lock().map_err(|e| {
48 ValueError::Runtime(RuntimeError {
49 code: "PYTHON_PACKAGING_POLICY",
50 message: format!("unable to obtain lock: {}", e),
51 label: label.to_string(),
52 })
53 })
54 }
55
56 pub fn apply_to_resource<T>(
62 &self,
63 label: &str,
64 type_values: &TypeValues,
65 call_stack: &mut CallStack,
66 value: &mut T,
67 ) -> ValueResult
68 where
69 T: TypedValue + ResourceCollectionContext + Clone,
70 {
71 let new_context = self
72 .inner(label)?
73 .derive_add_collection_context(&value.as_python_resource()?);
74 value.replace_add_collection_context(new_context)?;
75
76 for func in &self.derive_context_callbacks {
77 let temp_value = Value::new(value.clone());
89
90 func.call(
91 call_stack,
92 type_values,
93 vec![Value::new(self.clone()), temp_value.clone()],
94 LinkedHashMap::new(),
95 None,
96 None,
97 )?;
98
99 let downcast_value = temp_value.downcast_ref::<T>().unwrap();
100 let inner: &T = downcast_value.deref();
101 value.replace_add_collection_context(inner.add_collection_context()?.unwrap())?;
102 }
103
104 Ok(Value::from(NoneType::None))
105 }
106}
107
108impl TypedValue for PythonPackagingPolicyValue {
109 type Holder = Mutable<PythonPackagingPolicyValue>;
110 const TYPE: &'static str = "PythonPackagingPolicy";
111
112 fn values_for_descendant_check_and_freeze<'a>(
113 &'a self,
114 ) -> Box<dyn Iterator<Item = Value> + 'a> {
115 Box::new(self.derive_context_callbacks.iter().cloned())
116 }
117
118 fn get_attr(&self, attribute: &str) -> ValueResult {
119 let inner = self.inner(&format!("PythonPackagingPolicy.{}", attribute))?;
120
121 let v = match attribute {
122 "allow_files" => Value::from(inner.allow_files()),
123 "allow_in_memory_shared_library_loading" => {
124 Value::from(inner.allow_in_memory_shared_library_loading())
125 }
126 "bytecode_optimize_level_zero" => Value::from(inner.bytecode_optimize_level_zero()),
127 "bytecode_optimize_level_one" => Value::from(inner.bytecode_optimize_level_one()),
128 "bytecode_optimize_level_two" => Value::from(inner.bytecode_optimize_level_two()),
129 "extension_module_filter" => Value::from(inner.extension_module_filter().as_ref()),
130 "file_scanner_classify_files" => Value::from(inner.file_scanner_classify_files()),
131 "file_scanner_emit_files" => Value::from(inner.file_scanner_emit_files()),
132 "include_distribution_sources" => Value::from(inner.include_distribution_sources()),
133 "include_distribution_resources" => Value::from(inner.include_distribution_resources()),
134 "include_classified_resources" => Value::from(inner.include_classified_resources()),
135 "include_file_resources" => Value::from(inner.include_file_resources()),
136 "include_non_distribution_sources" => {
137 Value::from(inner.include_non_distribution_sources())
138 }
139 "include_test" => Value::from(inner.include_test()),
140 "preferred_extension_module_variants" => {
141 Value::try_from(inner.preferred_extension_module_variants().clone())?
142 }
143 "resources_location" => Value::from(inner.resources_location().to_string()),
144 "resources_location_fallback" => match inner.resources_location_fallback() {
145 Some(location) => Value::from(location.to_string()),
146 None => Value::from(NoneType::None),
147 },
148 attr => {
149 return Err(ValueError::OperationNotSupported {
150 op: UnsupportedOperation::GetAttr(attr.to_string()),
151 left: "PythonPackagingPolicy".to_string(),
152 right: None,
153 })
154 }
155 };
156
157 Ok(v)
158 }
159
160 fn has_attr(&self, attribute: &str) -> Result<bool, ValueError> {
161 Ok(matches!(
162 attribute,
163 "allow_files"
164 | "allow_in_memory_shared_library_loading"
165 | "bytecode_optimize_level_zero"
166 | "bytecode_optimize_level_one"
167 | "bytecode_optimize_level_two"
168 | "extension_module_filter"
169 | "file_scanner_classify_files"
170 | "file_scanner_emit_files"
171 | "include_distribution_sources"
172 | "include_distribution_resources"
173 | "include_classified_resources"
174 | "include_file_resources"
175 | "include_non_distribution_sources"
176 | "include_test"
177 | "preferred_extension_module_variants"
178 | "resources_location"
179 | "resources_location_fallback"
180 ))
181 }
182
183 fn set_attr(&mut self, attribute: &str, value: Value) -> Result<(), ValueError> {
184 let mut inner = self.inner(&format!("PythonPackagingPolicy.{}", attribute))?;
185
186 match attribute {
187 "allow_files" => {
188 inner.set_allow_files(value.to_bool());
189 }
190 "allow_in_memory_shared_library_loading" => {
191 inner.set_allow_in_memory_shared_library_loading(value.to_bool());
192 }
193 "bytecode_optimize_level_zero" => {
194 inner.set_bytecode_optimize_level_zero(value.to_bool());
195 }
196 "bytecode_optimize_level_one" => {
197 inner.set_bytecode_optimize_level_one(value.to_bool());
198 }
199 "bytecode_optimize_level_two" => {
200 inner.set_bytecode_optimize_level_two(value.to_bool());
201 }
202 "extension_module_filter" => {
203 let filter =
204 ExtensionModuleFilter::try_from(value.to_string().as_str()).map_err(|e| {
205 ValueError::from(RuntimeError {
206 code: "PYOXIDIZER_BUILD",
207 message: e,
208 label: format!("{}.{} = {}", Self::TYPE, attribute, value),
209 })
210 })?;
211
212 inner.set_extension_module_filter(filter);
213 }
214 "file_scanner_classify_files" => {
215 inner.set_file_scanner_classify_files(value.to_bool());
216 }
217 "file_scanner_emit_files" => {
218 inner.set_file_scanner_emit_files(value.to_bool());
219 }
220 "include_classified_resources" => {
221 inner.set_include_classified_resources(value.to_bool());
222 }
223 "include_distribution_sources" => {
224 inner.set_include_distribution_sources(value.to_bool());
225 }
226 "include_distribution_resources" => {
227 inner.set_include_distribution_resources(value.to_bool());
228 }
229 "include_file_resources" => {
230 inner.set_include_file_resources(value.to_bool());
231 }
232 "include_non_distribution_sources" => {
233 inner.set_include_non_distribution_sources(value.to_bool());
234 }
235 "include_test" => {
236 inner.set_include_test(value.to_bool());
237 }
238 "resources_location" => {
239 inner.set_resources_location(
240 ConcreteResourceLocation::try_from(value.to_string().as_str()).map_err(
241 |e| {
242 ValueError::from(RuntimeError {
243 code: "PYOXIDIZER_BUILD",
244 message: e,
245 label: format!("{}.{} = {}", Self::TYPE, attribute, value),
246 })
247 },
248 )?,
249 );
250 }
251 "resources_location_fallback" => {
252 if value.get_type() == "NoneType" {
253 inner.set_resources_location_fallback(None);
254 } else {
255 inner.set_resources_location_fallback(Some(
256 ConcreteResourceLocation::try_from(value.to_string().as_str()).map_err(
257 |e| {
258 ValueError::from(RuntimeError {
259 code: "PYOXIDIZER_BUILD",
260 message: e,
261 label: format!("{}.{} = {}", Self::TYPE, attribute, value),
262 })
263 },
264 )?,
265 ));
266 }
267 }
268 attr => {
269 return Err(ValueError::OperationNotSupported {
270 op: UnsupportedOperation::SetAttr(attr.to_string()),
271 left: Self::TYPE.to_owned(),
272 right: None,
273 });
274 }
275 }
276
277 Ok(())
278 }
279}
280
281impl PythonPackagingPolicyValue {
283 fn starlark_register_resource_callback(&mut self, func: &Value) -> ValueResult {
284 required_type_arg("func", "function", func)?;
285
286 self.derive_context_callbacks.push(func.clone());
287
288 Ok(Value::from(NoneType::None))
289 }
290
291 #[allow(clippy::unnecessary_wraps)]
292 fn starlark_set_preferred_extension_module_variant(
293 &mut self,
294 name: String,
295 value: String,
296 ) -> ValueResult {
297 self.inner("PythonPackagingPolicy.set_preferred_extension_module_variant()")?
298 .set_preferred_extension_module_variant(&name, &value);
299
300 Ok(Value::from(NoneType::None))
301 }
302
303 fn starlark_set_resource_handling_mode(&mut self, value: String) -> ValueResult {
304 const LABEL: &str = "PythonPackagingPolicy.set_resource_handling_mode()";
305
306 let mode = ResourceHandlingMode::try_from(value.as_str()).map_err(|e| {
307 ValueError::from(RuntimeError {
308 code: "PYTHON_PACKAGING_POLICY",
309 message: e,
310 label: LABEL.to_string(),
311 })
312 })?;
313
314 self.inner(LABEL)?.set_resource_handling_mode(mode);
315
316 Ok(Value::from(NoneType::None))
317 }
318}
319
320starlark_module! { python_packaging_policy_module =>
321 PythonPackagingPolicy.register_resource_callback(this, func) {
322 let mut this = this.downcast_mut::<PythonPackagingPolicyValue>().unwrap().unwrap();
323 this.starlark_register_resource_callback(&func)
324 }
325
326 PythonPackagingPolicy.set_preferred_extension_module_variant(
327 this,
328 name: String,
329 value: String
330 ) {
331 let mut this = this.downcast_mut::<PythonPackagingPolicyValue>().unwrap().unwrap();
332 this.starlark_set_preferred_extension_module_variant(name, value)
333 }
334
335 PythonPackagingPolicy.set_resource_handling_mode(this, mode: String) {
336 let mut this = this.downcast_mut::<PythonPackagingPolicyValue>().unwrap().unwrap();
337 this.starlark_set_resource_handling_mode(mode)
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use {
344 super::super::testutil::*,
345 super::super::{
346 python_distribution::PythonDistributionValue, python_executable::PythonExecutableValue,
347 },
348 super::*,
349 anyhow::Result,
350 indoc::indoc,
351 };
352
353 #[test]
354 fn test_basic() -> Result<()> {
355 let mut env = test_evaluation_context_builder()?.into_context()?;
356
357 env.eval("dist = default_python_distribution()")?;
358 env.eval("policy = dist.make_python_packaging_policy()")?;
359
360 let dist_value = env.eval("dist")?;
361 let dist = dist_value
362 .downcast_ref::<PythonDistributionValue>()
363 .unwrap();
364 let dist_ref = dist.distribution.as_ref().unwrap();
365
366 let policy = dist_ref.create_packaging_policy().unwrap();
367
368 {
370 let policy_value = env.eval("policy")?;
371 assert_eq!(policy_value.get_type(), "PythonPackagingPolicy");
372
373 let x = policy_value
374 .downcast_ref::<PythonPackagingPolicyValue>()
375 .unwrap();
376
377 assert_eq!(&policy, x.inner("test").unwrap().deref());
379 }
380
381 let value = env.eval("policy.extension_module_filter")?;
383 assert_eq!(value.get_type(), "string");
384 assert_eq!(value.to_string(), policy.extension_module_filter().as_ref());
385
386 let value =
387 env.eval("policy.extension_module_filter = 'minimal'; policy.extension_module_filter")?;
388 assert_eq!(value.to_string(), "minimal");
389
390 let value = env.eval("policy.file_scanner_classify_files")?;
391 assert_eq!(value.get_type(), "bool");
392 assert!(value.to_bool());
393
394 let value = env.eval(
395 "policy.file_scanner_classify_files = False; policy.file_scanner_classify_files",
396 )?;
397 assert_eq!(value.get_type(), "bool");
398 assert!(!value.to_bool());
399
400 let value = env.eval("policy.file_scanner_emit_files")?;
401 assert_eq!(value.get_type(), "bool");
402 assert!(!value.to_bool());
403
404 let value =
405 env.eval("policy.file_scanner_emit_files = True; policy.file_scanner_emit_files")?;
406 assert_eq!(value.get_type(), "bool");
407 assert!(value.to_bool());
408
409 let value = env.eval("policy.include_classified_resources")?;
410 assert_eq!(value.get_type(), "bool");
411 assert!(value.to_bool());
412
413 let value = env.eval(
414 "policy.include_classified_resources = False; policy.include_classified_resources",
415 )?;
416 assert_eq!(value.get_type(), "bool");
417 assert!(!value.to_bool());
418
419 let value = env.eval("policy.include_distribution_sources")?;
420 assert_eq!(value.get_type(), "bool");
421 assert_eq!(value.to_bool(), policy.include_distribution_sources());
422
423 let value = env.eval(
424 "policy.include_distribution_sources = False; policy.include_distribution_sources",
425 )?;
426 assert!(!value.to_bool());
427
428 let value = env.eval(
429 "policy.include_distribution_sources = True; policy.include_distribution_sources",
430 )?;
431 assert!(value.to_bool());
432
433 let value = env.eval("policy.include_distribution_resources")?;
434 assert_eq!(value.get_type(), "bool");
435 assert_eq!(value.to_bool(), policy.include_distribution_resources());
436
437 let value = env.eval(
438 "policy.include_distribution_resources = False; policy.include_distribution_resources",
439 )?;
440 assert!(!value.to_bool());
441
442 let value = env.eval(
443 "policy.include_distribution_resources = True; policy.include_distribution_resources",
444 )?;
445 assert!(value.to_bool());
446
447 let value = env.eval("policy.include_file_resources")?;
448 assert_eq!(value.get_type(), "bool");
449 assert!(!value.to_bool());
450
451 let value =
452 env.eval("policy.include_file_resources = True; policy.include_file_resources")?;
453 assert_eq!(value.get_type(), "bool");
454 assert!(value.to_bool());
455
456 let value = env.eval("policy.include_non_distribution_sources")?;
457 assert_eq!(value.get_type(), "bool");
458 assert_eq!(value.to_bool(), policy.include_non_distribution_sources());
459
460 let value = env.eval(
461 "policy.include_non_distribution_sources = False; policy.include_non_distribution_sources",
462 )?;
463 assert!(!value.to_bool());
464
465 let value = env.eval(
466 "policy.include_non_distribution_sources = True; policy.include_non_distribution_sources",
467 )?;
468 assert!(value.to_bool());
469
470 let value = env.eval("policy.include_test")?;
471 assert_eq!(value.get_type(), "bool");
472 assert_eq!(value.to_bool(), policy.include_test());
473
474 let value = env.eval("policy.include_test = False; policy.include_test")?;
475 assert!(!value.to_bool());
476
477 let value = env.eval("policy.include_test = True; policy.include_test")?;
478 assert!(value.to_bool());
479
480 let value = env.eval("policy.resources_location")?;
481 assert_eq!(value.get_type(), "string");
482 assert_eq!(value.to_string(), "in-memory");
483
484 let value = env.eval(
485 "policy.resources_location = 'filesystem-relative:lib'; policy.resources_location",
486 )?;
487 assert_eq!(value.to_string(), "filesystem-relative:lib");
488
489 let value = env.eval("policy.resources_location_fallback")?;
490 if dist_ref.supports_in_memory_shared_library_loading() {
491 assert_eq!(value.get_type(), "string");
492 assert_eq!(value.to_string(), "filesystem-relative:lib");
493 } else {
494 assert_eq!(value.get_type(), "NoneType");
495 }
496
497 let value = env.eval("policy.resources_location_fallback = 'filesystem-relative:lib'; policy.resources_location_fallback")?;
498 assert_eq!(value.to_string(), "filesystem-relative:lib");
499
500 let value = env.eval(
501 "policy.resources_location_fallback = None; policy.resources_location_fallback",
502 )?;
503 assert_eq!(value.get_type(), "NoneType");
504
505 let value = env.eval("policy.allow_files")?;
506 assert_eq!(value.get_type(), "bool");
507 assert!(!value.to_bool());
508
509 let value = env.eval("policy.allow_files = True; policy.allow_files")?;
510 assert_eq!(value.get_type(), "bool");
511 assert!(value.to_bool());
512
513 let value = env.eval("policy.allow_in_memory_shared_library_loading")?;
514 assert_eq!(value.get_type(), "bool");
515 assert!(!value.to_bool());
516
517 let value = env.eval("policy.allow_in_memory_shared_library_loading = True; policy.allow_in_memory_shared_library_loading")?;
518 assert_eq!(value.get_type(), "bool");
519 assert!(value.to_bool());
520
521 let value = env.eval("policy.bytecode_optimize_level_zero")?;
523 assert_eq!(value.get_type(), "bool");
524 assert_eq!(value.to_bool(), policy.bytecode_optimize_level_zero());
525
526 let value = env.eval(
527 "policy.bytecode_optimize_level_zero = False; policy.bytecode_optimize_level_zero",
528 )?;
529 assert!(!value.to_bool());
530
531 let value = env.eval(
532 "policy.bytecode_optimize_level_zero = True; policy.bytecode_optimize_level_zero",
533 )?;
534 assert!(value.to_bool());
535
536 let value = env.eval("policy.bytecode_optimize_level_one")?;
538 assert_eq!(value.get_type(), "bool");
539 assert_eq!(value.to_bool(), policy.bytecode_optimize_level_one());
540
541 let value = env.eval(
542 "policy.bytecode_optimize_level_one = False; policy.bytecode_optimize_level_one",
543 )?;
544 assert!(!value.to_bool());
545
546 let value = env.eval(
547 "policy.bytecode_optimize_level_one = True; policy.bytecode_optimize_level_one",
548 )?;
549 assert!(value.to_bool());
550
551 let value = env.eval("policy.bytecode_optimize_level_two")?;
553 assert_eq!(value.get_type(), "bool");
554 assert_eq!(value.to_bool(), policy.bytecode_optimize_level_two());
555
556 let value = env.eval(
557 "policy.bytecode_optimize_level_two = False; policy.bytecode_optimize_level_two",
558 )?;
559 assert!(!value.to_bool());
560
561 let value = env.eval(
562 "policy.bytecode_optimize_level_two = True; policy.bytecode_optimize_level_two",
563 )?;
564 assert!(value.to_bool());
565
566 Ok(())
567 }
568
569 #[test]
570 fn test_preferred_extension_module_variants() -> Result<()> {
571 let mut env = test_evaluation_context_builder()?.into_context()?;
572
573 env.eval("dist = default_python_distribution()")?;
574 env.eval("policy = dist.make_python_packaging_policy()")?;
575
576 let value = env.eval("policy.preferred_extension_module_variants")?;
577 assert_eq!(value.get_type(), "dict");
578 assert_eq!(value.length().unwrap(), 0);
579
580 env.eval("policy.set_preferred_extension_module_variant('foo', 'bar')")?;
581
582 let value = env.eval("policy.preferred_extension_module_variants")?;
583 assert_eq!(value.get_type(), "dict");
584 assert_eq!(value.length().unwrap(), 1);
585 assert_eq!(value.at(Value::from("foo")).unwrap(), Value::from("bar"));
586
587 Ok(())
588 }
589
590 #[test]
591 fn test_register_resource_callback() -> Result<()> {
592 let mut env = test_evaluation_context_builder()?.into_context()?;
593
594 env.eval("dist = default_python_distribution()")?;
595 env.eval("policy = dist.make_python_packaging_policy()")?;
596 env.eval("def my_func(policy, resource):\n return None")?;
597
598 env.eval("policy.register_resource_callback(my_func)")?;
599
600 let policy_value = env.eval("policy")?;
601 let policy = policy_value
602 .downcast_ref::<PythonPackagingPolicyValue>()
603 .unwrap();
604 assert_eq!(policy.derive_context_callbacks.len(), 1);
605
606 let func = policy.derive_context_callbacks[0].clone();
607 assert_eq!(func.get_type(), "function");
608 assert_eq!(func.to_str(), "my_func(policy, resource)");
609
610 Ok(())
611 }
612
613 #[test]
614 fn test_set_resource_handling_mode() -> Result<()> {
615 let mut env = test_evaluation_context_builder()?.into_context()?;
616
617 env.eval("dist = default_python_distribution()")?;
618 env.eval("policy = dist.make_python_packaging_policy()")?;
619
620 assert!(env
621 .eval("policy.set_resource_handling_mode('invalid')")
622 .is_err());
623
624 env.eval("policy.set_resource_handling_mode('classify')")?;
625 env.eval("policy.set_resource_handling_mode('files')")?;
626
627 Ok(())
628 }
629
630 #[test]
631 fn test_stdlib_extension_module_enable() -> Result<()> {
632 let mut env = test_evaluation_context_builder()?.into_context()?;
633
634 let exe_value = env.eval(indoc! {r#"
635 dist = default_python_distribution()
636 policy = dist.make_python_packaging_policy()
637 policy.extension_module_filter = "minimal"
638 policy.resources_location_fallback = "filesystem-relative:lib"
639
640 def cb(policy, resource):
641 if type(resource) == "PythonExtensionModule":
642 if resource.name == "_ssl":
643 resource.add_include = True
644
645 policy.register_resource_callback(cb)
646
647 exe = dist.to_python_executable(
648 name = "myapp",
649 packaging_policy = policy
650 )
651
652 exe
653 "#})?;
654
655 let exe = exe_value.downcast_ref::<PythonExecutableValue>().unwrap();
656 let inner = exe.inner("ignored").unwrap();
657
658 assert_eq!(
659 inner
660 .iter_resources()
661 .filter(|(_, r)| { r.is_extension_module && r.name == "_ssl" })
662 .count(),
663 0,
665 );
666
667 Ok(())
668 }
669
670 #[test]
671 fn test_stdlib_extension_module_disable() -> Result<()> {
672 let mut env = test_evaluation_context_builder()?.into_context()?;
673
674 let exe_value = env.eval(indoc! {r#"
675 dist = default_python_distribution()
676 policy = dist.make_python_packaging_policy()
677 policy.resources_location_fallback = "filesystem-relative:lib"
678
679 def cb(policy, resource):
680 if type(resource) == "PythonExtensionModule":
681 if resource.name == "_ssl":
682 resource.add_include = False
683
684 policy.register_resource_callback(cb)
685
686 exe = dist.to_python_executable(
687 name = "myapp",
688 packaging_policy = policy
689 )
690
691 exe
692 "#})?;
693
694 let exe = exe_value.downcast_ref::<PythonExecutableValue>().unwrap();
695 let inner = exe.inner("ignored").unwrap();
696
697 assert_eq!(
698 inner
699 .iter_resources()
700 .filter(|(_, r)| { r.is_extension_module && r.name == "_ssl" })
701 .count(),
702 if cfg!(windows) { 1 } else { 0 }
704 );
705
706 Ok(())
707 }
708
709 #[test]
710 fn test_ignore_non_stdlib_extension_module() -> Result<()> {
711 let mut env = test_evaluation_context_builder()?.into_context()?;
712
713 let exe_value = env.eval(indoc! {r#"
714 dist = default_python_distribution()
715 policy = dist.make_python_packaging_policy()
716 policy.resources_location_fallback = "filesystem-relative:lib"
717
718 exe = dist.to_python_executable(
719 name = "myapp",
720 packaging_policy = policy
721 )
722
723 exe.add_python_resources(exe.pip_install(["zstandard==0.16.0"]))
724
725 exe
726 "#})?;
727
728 let exe = exe_value.downcast_ref::<PythonExecutableValue>().unwrap();
729 let inner = exe.inner("ignored").unwrap();
730
731 assert_eq!(
732 inner
733 .iter_resources()
734 .filter(|(_, r)| { r.is_extension_module && r.name == "zstandard.backend_c" })
735 .count(),
736 1
737 );
738
739 let exe_value = env.eval(indoc! {r#"
740 dist = default_python_distribution()
741 policy = dist.make_python_packaging_policy()
742 policy.resources_location_fallback = "filesystem-relative:lib"
743
744 def cb(policy, resource):
745 if type(resource) == "PythonExtensionModule":
746 if resource.name == "zstandard.backend_c":
747 resource.add_include = False
748
749 policy.register_resource_callback(cb)
750
751 exe = dist.to_python_executable(
752 name = "myapp",
753 packaging_policy = policy,
754 )
755
756 exe.add_python_resources(exe.pip_install(["zstandard==0.16.0"]))
757
758 exe
759 "#})?;
760
761 let exe = exe_value.downcast_ref::<PythonExecutableValue>().unwrap();
762 let inner = exe.inner("ignored").unwrap();
763
764 assert_eq!(
765 inner
766 .iter_resources()
767 .filter(|(_, r)| { r.is_extension_module && r.name == "zstandard.backend_c" })
768 .count(),
769 1
771 );
772
773 Ok(())
774 }
775}