maturin 1.9.5__tar.gz → 1.9.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of maturin might be problematic. Click here for more details.

Files changed (64) hide show
  1. {maturin-1.9.5 → maturin-1.9.6}/.pre-commit-config.yaml +2 -2
  2. {maturin-1.9.5 → maturin-1.9.6}/Cargo.lock +1 -1
  3. {maturin-1.9.5 → maturin-1.9.6}/Cargo.toml +1 -1
  4. {maturin-1.9.5 → maturin-1.9.6}/Changelog.md +7 -1
  5. {maturin-1.9.5 → maturin-1.9.6}/PKG-INFO +1 -1
  6. {maturin-1.9.5 → maturin-1.9.6}/src/build_options.rs +18 -2
  7. {maturin-1.9.5 → maturin-1.9.6}/src/python_interpreter/mod.rs +232 -306
  8. {maturin-1.9.5 → maturin-1.9.6}/.cirrus.yml +0 -0
  9. {maturin-1.9.5 → maturin-1.9.6}/.codespellrc +0 -0
  10. {maturin-1.9.5 → maturin-1.9.6}/.config/nextest.toml +0 -0
  11. {maturin-1.9.5 → maturin-1.9.6}/.gitignore +0 -0
  12. {maturin-1.9.5 → maturin-1.9.6}/MANIFEST.in +0 -0
  13. {maturin-1.9.5 → maturin-1.9.6}/README.md +0 -0
  14. {maturin-1.9.5 → maturin-1.9.6}/clippy.toml +0 -0
  15. {maturin-1.9.5 → maturin-1.9.6}/license-apache +0 -0
  16. {maturin-1.9.5 → maturin-1.9.6}/license-mit +0 -0
  17. {maturin-1.9.5 → maturin-1.9.6}/maturin/__init__.py +0 -0
  18. {maturin-1.9.5 → maturin-1.9.6}/maturin/__main__.py +0 -0
  19. {maturin-1.9.5 → maturin-1.9.6}/maturin/bootstrap.py +0 -0
  20. {maturin-1.9.5 → maturin-1.9.6}/maturin.schema.json +0 -0
  21. {maturin-1.9.5 → maturin-1.9.6}/netlify.toml +0 -0
  22. {maturin-1.9.5 → maturin-1.9.6}/pyproject.toml +0 -0
  23. {maturin-1.9.5 → maturin-1.9.6}/setup.py +0 -0
  24. {maturin-1.9.5 → maturin-1.9.6}/src/auditwheel/audit.rs +0 -0
  25. {maturin-1.9.5 → maturin-1.9.6}/src/auditwheel/manylinux-policy.json +0 -0
  26. {maturin-1.9.5 → maturin-1.9.6}/src/auditwheel/mod.rs +0 -0
  27. {maturin-1.9.5 → maturin-1.9.6}/src/auditwheel/musllinux-policy.json +0 -0
  28. {maturin-1.9.5 → maturin-1.9.6}/src/auditwheel/musllinux.rs +0 -0
  29. {maturin-1.9.5 → maturin-1.9.6}/src/auditwheel/patchelf.rs +0 -0
  30. {maturin-1.9.5 → maturin-1.9.6}/src/auditwheel/platform_tag.rs +0 -0
  31. {maturin-1.9.5 → maturin-1.9.6}/src/auditwheel/policy.rs +0 -0
  32. {maturin-1.9.5 → maturin-1.9.6}/src/auditwheel/repair.rs +0 -0
  33. {maturin-1.9.5 → maturin-1.9.6}/src/bridge.rs +0 -0
  34. {maturin-1.9.5 → maturin-1.9.6}/src/build_context.rs +0 -0
  35. {maturin-1.9.5 → maturin-1.9.6}/src/cargo_toml.rs +0 -0
  36. {maturin-1.9.5 → maturin-1.9.6}/src/ci.rs +0 -0
  37. {maturin-1.9.5 → maturin-1.9.6}/src/compile.rs +0 -0
  38. {maturin-1.9.5 → maturin-1.9.6}/src/compression.rs +0 -0
  39. {maturin-1.9.5 → maturin-1.9.6}/src/cross_compile.rs +0 -0
  40. {maturin-1.9.5 → maturin-1.9.6}/src/develop.rs +0 -0
  41. {maturin-1.9.5 → maturin-1.9.6}/src/generate_json_schema.rs +0 -0
  42. {maturin-1.9.5 → maturin-1.9.6}/src/lib.rs +0 -0
  43. {maturin-1.9.5 → maturin-1.9.6}/src/main.rs +0 -0
  44. {maturin-1.9.5 → maturin-1.9.6}/src/metadata.rs +0 -0
  45. {maturin-1.9.5 → maturin-1.9.6}/src/module_writer.rs +0 -0
  46. {maturin-1.9.5 → maturin-1.9.6}/src/new_project.rs +0 -0
  47. {maturin-1.9.5 → maturin-1.9.6}/src/project_layout.rs +0 -0
  48. {maturin-1.9.5 → maturin-1.9.6}/src/pyproject_toml.rs +0 -0
  49. {maturin-1.9.5 → maturin-1.9.6}/src/python_interpreter/config.rs +0 -0
  50. {maturin-1.9.5 → maturin-1.9.6}/src/python_interpreter/get_interpreter_metadata.py +0 -0
  51. {maturin-1.9.5 → maturin-1.9.6}/src/source_distribution.rs +0 -0
  52. {maturin-1.9.5 → maturin-1.9.6}/src/target/legacy_py.rs +0 -0
  53. {maturin-1.9.5 → maturin-1.9.6}/src/target/mod.rs +0 -0
  54. {maturin-1.9.5 → maturin-1.9.6}/src/target/pypi_tags.rs +0 -0
  55. {maturin-1.9.5 → maturin-1.9.6}/src/templates/.gitignore.j2 +0 -0
  56. {maturin-1.9.5 → maturin-1.9.6}/src/templates/Cargo.toml.j2 +0 -0
  57. {maturin-1.9.5 → maturin-1.9.6}/src/templates/__init__.py.j2 +0 -0
  58. {maturin-1.9.5 → maturin-1.9.6}/src/templates/build.rs.j2 +0 -0
  59. {maturin-1.9.5 → maturin-1.9.6}/src/templates/example.udl.j2 +0 -0
  60. {maturin-1.9.5 → maturin-1.9.6}/src/templates/lib.rs.j2 +0 -0
  61. {maturin-1.9.5 → maturin-1.9.6}/src/templates/main.rs.j2 +0 -0
  62. {maturin-1.9.5 → maturin-1.9.6}/src/templates/pyproject.toml.j2 +0 -0
  63. {maturin-1.9.5 → maturin-1.9.6}/src/templates/test_all.py.j2 +0 -0
  64. {maturin-1.9.5 → maturin-1.9.6}/src/upload.rs +0 -0
@@ -56,12 +56,12 @@ repos:
56
56
  )
57
57
  - id: mixed-line-ending
58
58
  - repo: https://github.com/astral-sh/ruff-pre-commit
59
- rev: v0.13.0
59
+ rev: v0.13.3
60
60
  hooks:
61
61
  - id: ruff-format
62
62
  - id: ruff
63
63
  - repo: https://github.com/pre-commit/mirrors-mypy
64
- rev: v1.18.1
64
+ rev: v1.18.2
65
65
  hooks:
66
66
  - id: mypy
67
67
  entry: mypy maturin/
@@ -1455,7 +1455,7 @@ dependencies = [
1455
1455
 
1456
1456
  [[package]]
1457
1457
  name = "maturin"
1458
- version = "1.9.5"
1458
+ version = "1.9.6"
1459
1459
  dependencies = [
1460
1460
  "anyhow",
1461
1461
  "base64 0.21.7",
@@ -1,7 +1,7 @@
1
1
  [package]
2
2
  authors = ["konstin <konstin@mailbox.org>", "messense <messense@icloud.com>"]
3
3
  name = "maturin"
4
- version = "1.9.5"
4
+ version = "1.9.6"
5
5
  description = "Build and publish crates with pyo3, cffi and uniffi bindings as well as rust binaries as python packages"
6
6
  exclude = [
7
7
  "test-crates/**/*",
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.9.6]
4
+
5
+ * Fix regressions to interpreter platform detection on Windows from 1.9.5
6
+ * Further fixes to interpreter detection for Windows ARM64
7
+
3
8
  ## [1.9.5]
4
9
 
5
10
  * Add builtin sysconfig for FreeBSD 14.2 amd64
@@ -1104,7 +1109,8 @@ points-0.1.0-py2.py3-none-manylinux1_x86_64.whl | 2,8M | 752K | 85K
1104
1109
 
1105
1110
  * Initial Release
1106
1111
 
1107
- [Unreleased]: https://github.com/pyo3/maturin/compare/v1.9.5...HEAD
1112
+ [Unreleased]: https://github.com/pyo3/maturin/compare/v1.9.6...HEAD
1113
+ [1.9.6]: https://github.com/pyo3/maturin/compare/v1.9.5...v1.9.6
1108
1114
  [1.9.5]: https://github.com/pyo3/maturin/compare/v1.9.4...v1.9.5
1109
1115
  [1.9.4]: https://github.com/pyo3/maturin/compare/v1.9.3...v1.9.4
1110
1116
  [1.9.3]: https://github.com/pyo3/maturin/compare/v1.9.2...v1.9.3
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maturin
3
- Version: 1.9.5
3
+ Version: 1.9.6
4
4
  Classifier: Topic :: Software Development :: Build Tools
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: Programming Language :: Python :: Implementation :: CPython
@@ -374,8 +374,13 @@ impl BuildOptions {
374
374
  }
375
375
  }
376
376
  } else {
377
- interpreters =
378
- find_interpreter(bridge, interpreter, target, requires_python)?;
377
+ interpreters = find_interpreter(
378
+ bridge,
379
+ interpreter,
380
+ target,
381
+ requires_python,
382
+ generate_import_lib,
383
+ )?;
379
384
  }
380
385
 
381
386
  let interpreters_str = interpreters
@@ -1288,6 +1293,7 @@ fn find_interpreter(
1288
1293
  interpreter: &[PathBuf],
1289
1294
  target: &Target,
1290
1295
  requires_python: Option<&VersionSpecifiers>,
1296
+ generate_import_lib: bool,
1291
1297
  ) -> Result<Vec<PythonInterpreter>> {
1292
1298
  let mut found_interpreters = Vec::new();
1293
1299
  if !interpreter.is_empty() {
@@ -1301,6 +1307,16 @@ fn find_interpreter(
1301
1307
  if !missing.is_empty() {
1302
1308
  let sysconfig_interps =
1303
1309
  find_interpreter_in_sysconfig(bridge, &missing, target, requires_python)?;
1310
+
1311
+ // Can only use sysconfig-derived interpreter on windows if generating the import lib
1312
+ if !sysconfig_interps.is_empty() && target.is_windows() && !generate_import_lib {
1313
+ let found = sysconfig_interps
1314
+ .iter()
1315
+ .map(|i| format!("{} {}.{}", i.interpreter_kind, i.major, i.minor))
1316
+ .collect::<Vec<_>>();
1317
+ bail!("Interpreters {found:?} were found in maturin's bundled sysconfig, but compiling for Windows without an interpreter requires PyO3's `generate-import-lib` feature");
1318
+ }
1319
+
1304
1320
  found_interpreters.extend(sysconfig_interps);
1305
1321
  }
1306
1322
  } else {
@@ -26,55 +26,6 @@ pub const MINIMUM_PYPY_MINOR: usize = 8;
26
26
  pub const MAXIMUM_PYTHON_MINOR: usize = 13;
27
27
  pub const MAXIMUM_PYPY_MINOR: usize = 11;
28
28
 
29
- /// Identifies conditions where we do not want to build wheels
30
- fn windows_interpreter_no_build(
31
- major: usize,
32
- minor: usize,
33
- target: &Target,
34
- platform: String,
35
- min_python_minor: usize,
36
- requires_python: Option<&VersionSpecifiers>,
37
- ) -> bool {
38
- // Only python 3 with supported major versions
39
- if major != 3 || minor < min_python_minor {
40
- return true;
41
- }
42
-
43
- // From requires-python in pyproject.toml
44
- if let Some(requires_python) = requires_python {
45
- if !requires_python.contains(&Version::new([major as u64, minor as u64])) {
46
- return true;
47
- }
48
- }
49
-
50
- let python_arch = match platform.as_str() {
51
- "win32" => Arch::X86,
52
- "win-amd64" => Arch::X86_64,
53
- "win-arm64" => Arch::Aarch64,
54
- _ => {
55
- eprintln!("⚠️ Warning: {major}.{minor} reports unknown platform '{platform}'. This may fail to build.");
56
- // false => build it anyway
57
- return false;
58
- }
59
- };
60
-
61
- let target_arch = target.target_arch();
62
-
63
- if python_arch != target.target_arch() {
64
- eprintln!(
65
- "👽 {major}.{minor} reports a platform '{platform}' (architecture '{python_arch}'), while the Rust target is '{target_arch}'. Skipping."
66
- );
67
- return true;
68
- }
69
- false
70
- }
71
-
72
- struct WindowsPythonInfo {
73
- major: usize,
74
- minor: usize,
75
- platform: String, // e.g. win32, win-amd64, win-arm64
76
- }
77
-
78
29
  /// On windows regular Python installs are supported along with environments
79
30
  /// being managed by `conda`.
80
31
  ///
@@ -116,12 +67,32 @@ fn find_all_windows(
116
67
  target: &Target,
117
68
  bridge: &BridgeModel,
118
69
  requires_python: Option<&VersionSpecifiers>,
119
- ) -> Result<Vec<String>> {
70
+ ) -> Result<Vec<PythonInterpreter>> {
120
71
  let min_python_minor = bridge.minimal_python_minor_version();
121
- let code = "import sys; print(sys.executable or '')";
122
72
  let mut interpreter = vec![];
123
73
  let mut versions_found = HashSet::new();
124
74
 
75
+ macro_rules! maybe_add_interp {
76
+ ($executable:expr) => {
77
+ PythonInterpreter::check_executable($executable, target, bridge).map(|interp| {
78
+ if let Some(interp) = interp {
79
+ let major = interp.major;
80
+ let minor = interp.minor;
81
+ if major == 3
82
+ && minor >= min_python_minor
83
+ && !versions_found.contains(&(major, minor))
84
+ && requires_python.map_or(true, |requires_python| {
85
+ requires_python.contains(&Version::new([major as u64, minor as u64]))
86
+ })
87
+ {
88
+ interpreter.push(interp);
89
+ versions_found.insert((major, minor));
90
+ }
91
+ }
92
+ })
93
+ };
94
+ }
95
+
125
96
  // If Python is installed from Python.org it should include the "python launcher"
126
97
  // which is used to find the installed interpreters
127
98
  let execution = Command::new("cmd")
@@ -156,38 +127,7 @@ fn find_all_windows(
156
127
  if !executable_path.exists() {
157
128
  continue;
158
129
  }
159
- if let Some(python_info) = windows_python_info(executable_path)? {
160
- if windows_interpreter_no_build(
161
- major,
162
- minor,
163
- target,
164
- python_info.platform,
165
- min_python_minor,
166
- requires_python,
167
- ) {
168
- continue;
169
- }
170
-
171
- let output = Command::new(executable).args(["-c", code]).output();
172
- let output = match output {
173
- Ok(output) => output,
174
- Err(err) => {
175
- eprintln!(
176
- "⚠️ Warning: failed to determine the path to python for `{executable}`: {err}"
177
- );
178
- continue;
179
- }
180
- };
181
- let path = str::from_utf8(&output.stdout).unwrap().trim();
182
- if !output.status.success() || path.trim().is_empty() {
183
- eprintln!(
184
- "⚠️ Warning: couldn't determine the path to python for `{executable}`"
185
- );
186
- continue;
187
- }
188
- interpreter.push(path.to_string());
189
- versions_found.insert((major, minor));
190
- }
130
+ maybe_add_interp!(executable_path)?;
191
131
  }
192
132
  }
193
133
  }
@@ -218,20 +158,7 @@ fn find_all_windows(
218
158
  } else {
219
159
  Path::new(&path).join("python")
220
160
  };
221
- if let Some(python_info) = windows_python_info(&executable)? {
222
- if windows_interpreter_no_build(
223
- python_info.major,
224
- python_info.minor,
225
- target,
226
- python_info.platform,
227
- min_python_minor,
228
- requires_python,
229
- ) {
230
- continue;
231
- }
232
- interpreter.push(String::from(executable.to_str().unwrap()));
233
- versions_found.insert((python_info.major, python_info.minor));
234
- }
161
+ maybe_add_interp!(executable.as_path())?;
235
162
  }
236
163
  }
237
164
 
@@ -239,20 +166,7 @@ fn find_all_windows(
239
166
  for minor in min_python_minor..=bridge.maximum_python_minor_version() {
240
167
  if !versions_found.contains(&(3, minor)) {
241
168
  let executable = format!("python3.{minor}.exe");
242
- if let Some(python_info) = windows_python_info(Path::new(&executable))? {
243
- if windows_interpreter_no_build(
244
- python_info.major,
245
- python_info.minor,
246
- target,
247
- python_info.platform,
248
- min_python_minor,
249
- requires_python,
250
- ) {
251
- continue;
252
- }
253
- interpreter.push(executable);
254
- versions_found.insert((3, minor));
255
- }
169
+ maybe_add_interp!(Path::new(&executable))?;
256
170
  }
257
171
  }
258
172
 
@@ -264,49 +178,6 @@ fn find_all_windows(
264
178
  Ok(interpreter)
265
179
  }
266
180
 
267
- fn windows_python_info(executable: &Path) -> Result<Option<WindowsPythonInfo>> {
268
- let python_info = Command::new(executable)
269
- .arg("-c")
270
- .arg("import sys, sysconfig; print(sys.version_info.major, sys.version_info.minor, sysconfig.get_platform())")
271
- .output();
272
-
273
- let python_info = match python_info {
274
- Ok(python_info) => python_info,
275
- Err(err) => {
276
- if err.kind() == io::ErrorKind::NotFound {
277
- // python executable not found
278
- return Ok(None);
279
- } else {
280
- bail!(
281
- "Error getting Python version info from {}",
282
- executable.display()
283
- );
284
- }
285
- }
286
- };
287
-
288
- let version_info = str::from_utf8(&python_info.stdout).unwrap();
289
-
290
- // Split into 3 segments: major, minor, platform by spaces
291
- let segments: Vec<&str> = version_info.splitn(3, ' ').collect();
292
- let [major, minor, platform] = segments.as_slice() else {
293
- bail!(
294
- "Unexpected output for Python version info from {}: '{}'",
295
- executable.display(),
296
- version_info
297
- );
298
- };
299
- // can then parse each substring
300
- let major = major.parse::<usize>().ok().unwrap_or(0);
301
- let minor = minor.parse::<usize>().ok().unwrap_or(0);
302
-
303
- Ok(Some(WindowsPythonInfo {
304
- major,
305
- minor,
306
- platform: platform.to_string(),
307
- }))
308
- }
309
-
310
181
  #[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize, clap::ValueEnum)]
311
182
  #[serde(rename_all = "lowercase")]
312
183
  #[clap(rename_all = "lower")]
@@ -644,9 +515,15 @@ impl PythonInterpreter {
644
515
  let mut metadata_py = tempfile::NamedTempFile::new()?;
645
516
  write!(metadata_py, "{GET_INTERPRETER_METADATA}")?;
646
517
  let mut cmd = Command::new("cmd");
518
+ let suffix = match target.target_arch() {
519
+ Arch::X86 => "-32",
520
+ Arch::X86_64 => "-64",
521
+ Arch::Aarch64 => "-arm64",
522
+ _ => "",
523
+ };
647
524
  cmd.arg("/c")
648
525
  .arg("py")
649
- .arg(format!("-{}-{}", ver, target.pointer_width()))
526
+ .arg(format!("-{ver}{suffix}"))
650
527
  .arg(metadata_py.path())
651
528
  .env("PYTHONNOUSERSITE", "1");
652
529
  let output = cmd.output();
@@ -669,6 +546,18 @@ impl PythonInterpreter {
669
546
  .context(err_msg)
670
547
  .context(String::from_utf8_lossy(&output.stdout).trim().to_string())?;
671
548
 
549
+ Self::from_metadata_message(executable, target, bridge, message)
550
+ }
551
+
552
+ /// Configure a `PythonInterpreter` from the metadata message.
553
+ ///
554
+ /// Returns `None` if the interpreter is not suitable to use (e.g. too old or wrong architecture)
555
+ fn from_metadata_message(
556
+ executable: impl AsRef<Path>,
557
+ target: &Target,
558
+ bridge: &BridgeModel,
559
+ message: InterpreterMetadataMessage,
560
+ ) -> Result<Option<PythonInterpreter>> {
672
561
  if (message.major == 2 && message.minor != 7) || (message.major == 3 && message.minor < 5) {
673
562
  debug!(
674
563
  "Skipping outdated python interpreter '{}'",
@@ -691,6 +580,41 @@ impl PythonInterpreter {
691
580
  executable.as_ref().display()
692
581
  ))?;
693
582
 
583
+ let executable = message
584
+ .executable
585
+ .map(PathBuf::from)
586
+ .unwrap_or_else(|| executable.as_ref().to_path_buf());
587
+
588
+ if target.is_windows() {
589
+ 'windows_arch_check: {
590
+ // on windows we must check the architecture, because three different architectures
591
+ // can all run on the same hardware
592
+ let python_arch = match message.platform.as_str().trim() {
593
+ "win32" => Arch::X86,
594
+ "win-amd64" => Arch::X86_64,
595
+ "win-arm64" => Arch::Aarch64,
596
+ _ => {
597
+ eprintln!(
598
+ "⚠️ Warning: '{}' reports unknown platform. This may fail to build.",
599
+ executable.display()
600
+ );
601
+ break 'windows_arch_check;
602
+ }
603
+ };
604
+
605
+ if python_arch != target.target_arch() {
606
+ eprintln!(
607
+ "👽 '{}' reports a platform '{platform}' (architecture '{python_arch}'), while the Rust target is '{target_arch}'. Skipping.",
608
+ executable.display(),
609
+ platform = message.platform,
610
+ python_arch = python_arch,
611
+ target_arch = target.target_arch(),
612
+ );
613
+ return Ok(None);
614
+ }
615
+ }
616
+ }
617
+
694
618
  let platform = if message.platform.starts_with("macosx") {
695
619
  // We don't use platform from sysconfig on macOS
696
620
  None
@@ -698,10 +622,6 @@ impl PythonInterpreter {
698
622
  Some(message.platform.to_lowercase().replace(['-', '.'], "_"))
699
623
  };
700
624
 
701
- let executable = message
702
- .executable
703
- .map(PathBuf::from)
704
- .unwrap_or_else(|| executable.as_ref().to_path_buf());
705
625
  debug!(
706
626
  "Found {} interpreter at {}",
707
627
  interpreter,
@@ -807,37 +727,38 @@ impl PythonInterpreter {
807
727
  bridge: &BridgeModel,
808
728
  requires_python: Option<&VersionSpecifiers>,
809
729
  ) -> Result<Vec<PythonInterpreter>> {
810
- let executables = if target.is_windows() {
730
+ if target.is_windows() {
811
731
  // TOFIX: add PyPy support to Windows
812
- find_all_windows(target, bridge, requires_python)?
813
- } else {
814
- let mut executables: Vec<String> = (bridge.minimal_python_minor_version()
815
- ..=bridge.maximum_python_minor_version())
816
- .filter(|minor| {
817
- requires_python
818
- .map(|requires_python| {
819
- requires_python.contains(&Version::new([3, *minor as u64]))
820
- })
821
- .unwrap_or(true)
822
- })
823
- .map(|minor| format!("python3.{minor}"))
824
- .collect();
825
- // Also try to find PyPy for cffi and pyo3 bindings
826
- if *bridge == BridgeModel::Cffi || bridge.is_pyo3() {
827
- executables.extend(
828
- (bridge.minimal_pypy_minor_version()..=bridge.maximum_pypy_minor_version())
829
- .filter(|minor| {
830
- requires_python
831
- .map(|requires_python| {
832
- requires_python.contains(&Version::new([3, *minor as u64]))
833
- })
834
- .unwrap_or(true)
835
- })
836
- .map(|minor| format!("pypy3.{minor}")),
837
- );
838
- }
839
- executables
732
+ return find_all_windows(target, bridge, requires_python);
840
733
  };
734
+
735
+ let mut executables: Vec<String> = (bridge.minimal_python_minor_version()
736
+ ..=bridge.maximum_python_minor_version())
737
+ .filter(|minor| {
738
+ requires_python
739
+ .map(|requires_python| {
740
+ requires_python.contains(&Version::new([3, *minor as u64]))
741
+ })
742
+ .unwrap_or(true)
743
+ })
744
+ .map(|minor| format!("python3.{minor}"))
745
+ .collect();
746
+
747
+ // Also try to find PyPy for cffi and pyo3 bindings
748
+ if *bridge == BridgeModel::Cffi || bridge.is_pyo3() {
749
+ executables.extend(
750
+ (bridge.minimal_pypy_minor_version()..=bridge.maximum_pypy_minor_version())
751
+ .filter(|minor| {
752
+ requires_python
753
+ .map(|requires_python| {
754
+ requires_python.contains(&Version::new([3, *minor as u64]))
755
+ })
756
+ .unwrap_or(true)
757
+ })
758
+ .map(|minor| format!("pypy3.{minor}")),
759
+ );
760
+ }
761
+
841
762
  let mut available_versions = Vec::new();
842
763
  for executable in executables {
843
764
  if let Some(version) = PythonInterpreter::check_executable(executable, target, bridge)?
@@ -1195,135 +1116,140 @@ mod tests {
1195
1116
  }
1196
1117
 
1197
1118
  #[test]
1198
- fn test_windows_interpreter_no_build() {
1199
- use pep440_rs::VersionSpecifiers;
1200
- use std::str::FromStr;
1201
-
1119
+ fn test_interpreter_from_metadata_windows() {
1202
1120
  // Test cases for different scenarios
1203
1121
  let target_x64 = Target::from_resolved_target_triple("x86_64-pc-windows-msvc").unwrap();
1204
1122
  let target_x86 = Target::from_resolved_target_triple("i686-pc-windows-msvc").unwrap();
1205
1123
  let target_arm64 = Target::from_resolved_target_triple("aarch64-pc-windows-msvc").unwrap();
1206
1124
 
1125
+ let bridge = BridgeModel::PyO3(PyO3 {
1126
+ crate_name: PyO3Crate::PyO3,
1127
+ version: semver::Version::new(0, 26, 0),
1128
+ abi3: None,
1129
+ metadata: None,
1130
+ });
1131
+
1132
+ let message = |major, minor, platform: &str| InterpreterMetadataMessage {
1133
+ major,
1134
+ minor,
1135
+ interpreter: "cpython".to_string(),
1136
+ implementation_name: "CPython".to_string(),
1137
+ abiflags: None,
1138
+ ext_suffix: Some(".pyd".to_string()),
1139
+ platform: platform.to_string(),
1140
+ executable: None,
1141
+ soabi: None,
1142
+ gil_disabled: false,
1143
+ system: "windows".to_string(),
1144
+ };
1145
+
1207
1146
  // Test Python 2.x should be rejected
1208
- assert!(windows_interpreter_no_build(
1209
- 2,
1210
- 7,
1211
- &target_x64,
1212
- "win-amd64".to_string(),
1213
- 7,
1214
- None
1215
- ));
1147
+ assert_eq!(
1148
+ PythonInterpreter::from_metadata_message(
1149
+ "python2.7",
1150
+ &target_x64,
1151
+ &bridge,
1152
+ message(2, 7, "win-amd64"),
1153
+ )
1154
+ .unwrap_err()
1155
+ .to_string(),
1156
+ "Failed to get information from the python interpreter at python2.7"
1157
+ );
1216
1158
 
1217
1159
  // Test Python 3.x but below minimum version
1218
- assert!(windows_interpreter_no_build(
1219
- 3,
1220
- 6,
1221
- &target_x64,
1222
- "win-amd64".to_string(),
1223
- 7,
1224
- None
1225
- ));
1160
+ assert_eq!(
1161
+ PythonInterpreter::from_metadata_message(
1162
+ "python3.6",
1163
+ &target_x64,
1164
+ &bridge,
1165
+ message(3, 6, "win-amd64"),
1166
+ )
1167
+ .unwrap_err()
1168
+ .to_string(),
1169
+ "Failed to get information from the python interpreter at python3.6"
1170
+ );
1226
1171
 
1227
1172
  // Test valid Python version with matching platform and architecture
1228
- assert!(!windows_interpreter_no_build(
1229
- 3,
1230
- 10,
1231
- &target_x64,
1232
- "win-amd64".to_string(),
1233
- 7,
1234
- None
1235
- ));
1236
-
1237
- // Test 32-bit Python on 64-bit target (should be rejected)
1238
- assert!(windows_interpreter_no_build(
1239
- 3,
1240
- 10,
1241
- &target_x64,
1242
- "win32".to_string(),
1243
- 7,
1244
- None
1245
- ));
1246
-
1247
- // Test 32-bit Python on 32-bit target (should be accepted)
1248
- assert!(!windows_interpreter_no_build(
1249
- 3,
1250
- 10,
1251
- &target_x86,
1252
- "win32".to_string(),
1253
- 7,
1254
- None
1255
- ));
1173
+ for (target, platform) in &[
1174
+ (&target_x86, "win32"),
1175
+ (&target_x64, "win-amd64"),
1176
+ (&target_arm64, "win-arm64"),
1177
+ ] {
1178
+ assert_eq!(
1179
+ PythonInterpreter::from_metadata_message(
1180
+ "python3.10",
1181
+ target,
1182
+ &bridge,
1183
+ message(3, 10, platform),
1184
+ )
1185
+ .unwrap()
1186
+ .unwrap(),
1187
+ PythonInterpreter {
1188
+ config: InterpreterConfig {
1189
+ major: 3,
1190
+ minor: 10,
1191
+ interpreter_kind: InterpreterKind::CPython,
1192
+ abiflags: "".to_string(),
1193
+ ext_suffix: ".pyd".to_string(),
1194
+ pointer_width: None,
1195
+ gil_disabled: false,
1196
+ },
1197
+ executable: PathBuf::from("python3.10"),
1198
+ platform: Some(platform.replace("-", "_")),
1199
+ runnable: true,
1200
+ implementation_name: "CPython".to_string(),
1201
+ soabi: None,
1202
+ }
1203
+ );
1204
+ }
1256
1205
 
1257
1206
  // Test mismatched architectures
1258
- assert!(windows_interpreter_no_build(
1259
- 3,
1260
- 10,
1261
- &target_x64,
1262
- "win-arm64".to_string(),
1263
- 7,
1264
- None
1265
- ));
1266
-
1267
- assert!(windows_interpreter_no_build(
1268
- 3,
1269
- 10,
1270
- &target_arm64,
1271
- "win-amd64".to_string(),
1272
- 7,
1273
- None
1274
- ));
1275
-
1276
- // Test correct architecture matches
1277
- assert!(!windows_interpreter_no_build(
1278
- 3,
1279
- 10,
1280
- &target_arm64,
1281
- "win-arm64".to_string(),
1282
- 7,
1283
- None
1284
- ));
1285
-
1286
- // Test requires-python constraints
1287
- let requires_python = VersionSpecifiers::from_str(">=3.8,<3.12").unwrap();
1288
-
1289
- // Should reject Python 3.7 due to requires-python
1290
- assert!(windows_interpreter_no_build(
1291
- 3,
1292
- 7,
1293
- &target_x64,
1294
- "win-amd64".to_string(),
1295
- 7,
1296
- Some(&requires_python)
1297
- ));
1298
-
1299
- // Should accept Python 3.10 within requires-python range
1300
- assert!(!windows_interpreter_no_build(
1301
- 3,
1302
- 10,
1303
- &target_x64,
1304
- "win-amd64".to_string(),
1305
- 7,
1306
- Some(&requires_python)
1307
- ));
1308
-
1309
- // Should reject Python 3.12 due to requires-python upper bound
1310
- assert!(windows_interpreter_no_build(
1311
- 3,
1312
- 12,
1313
- &target_x64,
1314
- "win-amd64".to_string(),
1315
- 7,
1316
- Some(&requires_python)
1317
- ));
1318
-
1319
- // Test edge case with unknown platform (should not match any specific architecture)
1320
- assert!(!windows_interpreter_no_build(
1321
- 3,
1322
- 10,
1323
- &target_x64,
1324
- "unknown-platform".to_string(),
1325
- 7,
1326
- None
1327
- ));
1207
+ for (target, platform) in &[
1208
+ (&target_x86, "win-amd64"),
1209
+ (&target_x86, "win-arm64"),
1210
+ (&target_x64, "win32"),
1211
+ (&target_x64, "win-arm64"),
1212
+ (&target_arm64, "win32"),
1213
+ (&target_arm64, "win-amd64"),
1214
+ ] {
1215
+ assert_eq!(
1216
+ PythonInterpreter::from_metadata_message(
1217
+ "python3.10",
1218
+ target,
1219
+ &bridge,
1220
+ message(3, 10, platform),
1221
+ )
1222
+ .unwrap(),
1223
+ None
1224
+ );
1225
+ }
1226
+
1227
+ // Test edge case with unknown platform (should not match any specific architecture, build anyway)
1228
+ assert_eq!(
1229
+ PythonInterpreter::from_metadata_message(
1230
+ "python3.10",
1231
+ &target_x64,
1232
+ &bridge,
1233
+ message(3, 10, "unknown-platform"),
1234
+ )
1235
+ .unwrap()
1236
+ .unwrap(),
1237
+ PythonInterpreter {
1238
+ config: InterpreterConfig {
1239
+ major: 3,
1240
+ minor: 10,
1241
+ interpreter_kind: InterpreterKind::CPython,
1242
+ abiflags: "".to_string(),
1243
+ ext_suffix: ".pyd".to_string(),
1244
+ pointer_width: None,
1245
+ gil_disabled: false,
1246
+ },
1247
+ executable: PathBuf::from("python3.10"),
1248
+ platform: Some("unknown_platform".to_string()),
1249
+ runnable: true,
1250
+ implementation_name: "CPython".to_string(),
1251
+ soabi: None,
1252
+ }
1253
+ );
1328
1254
  }
1329
1255
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes