From feba308863e7b5effa3b1cffeb715077741efde6 Mon Sep 17 00:00:00 2001 From: Weijie Guo Date: Wed, 3 Apr 2024 16:36:20 +0800 Subject: [PATCH 1/3] Change `search_lib_dir`'s return type to Result --- pyo3-build-config/src/impl_.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index d5373db9655..62c906ce2ec 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -1211,7 +1211,7 @@ fn ends_with(entry: &DirEntry, pat: &str) -> bool { /// Returns `None` if the library directory is not available, and a runtime error /// when no or multiple sysconfigdata files are found. fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result> { - let mut sysconfig_paths = find_all_sysconfigdata(cross); + let mut sysconfig_paths = find_all_sysconfigdata(cross)?; if sysconfig_paths.is_empty() { if let Some(lib_dir) = cross.lib_dir.as_ref() { bail!("Could not find _sysconfigdata*.py in {}", lib_dir.display()); @@ -1274,11 +1274,11 @@ fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result> { /// /// Returns an empty vector when the target Python library directory /// is not set via `PYO3_CROSS_LIB_DIR`. -pub fn find_all_sysconfigdata(cross: &CrossCompileConfig) -> Vec { +pub fn find_all_sysconfigdata(cross: &CrossCompileConfig) -> Result> { let sysconfig_paths = if let Some(lib_dir) = cross.lib_dir.as_ref() { - search_lib_dir(lib_dir, cross) + search_lib_dir(lib_dir, cross)? } else { - return Vec::new(); + return Ok(Vec::new()); }; let sysconfig_name = env_var("_PYTHON_SYSCONFIGDATA_NAME"); @@ -1296,7 +1296,7 @@ pub fn find_all_sysconfigdata(cross: &CrossCompileConfig) -> Vec { sysconfig_paths.sort(); sysconfig_paths.dedup(); - sysconfig_paths + Ok(sysconfig_paths) } fn is_pypy_lib_dir(path: &str, v: &Option) -> bool { @@ -1327,9 +1327,11 @@ fn is_cpython_lib_dir(path: &str, v: &Option) -> bool { } /// recursive search for _sysconfigdata, returns all possibilities of sysconfigdata paths -fn search_lib_dir(path: impl AsRef, cross: &CrossCompileConfig) -> Vec { +fn search_lib_dir(path: impl AsRef, cross: &CrossCompileConfig) -> Result> { let mut sysconfig_paths = vec![]; - for f in fs::read_dir(path).expect("Path does not exist") { + for f in fs::read_dir(path) + .context("failed to search the lib dir, please check your path and permissions.")? + { sysconfig_paths.extend(match &f { // Python 3.7+ sysconfigdata with platform specifics Ok(f) if starts_with(f, "_sysconfigdata_") && ends_with(f, "py") => vec![f.path()], @@ -1337,7 +1339,7 @@ fn search_lib_dir(path: impl AsRef, cross: &CrossCompileConfig) -> Vec, cross: &CrossCompileConfig) -> Vec, cross: &CrossCompileConfig) -> Vec Date: Wed, 3 Apr 2024 16:41:28 +0800 Subject: [PATCH 2/3] add changelog --- newsfragments/4043.fixed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/4043.fixed.md diff --git a/newsfragments/4043.fixed.md b/newsfragments/4043.fixed.md new file mode 100644 index 00000000000..7653eb295bf --- /dev/null +++ b/newsfragments/4043.fixed.md @@ -0,0 +1 @@ +Don't panic when `PYO3_CROSS_LIB_DIR` is set to a missing path. \ No newline at end of file From bc864bc385b791454d4723e52e379d4aa84ebb5d Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sat, 22 Jun 2024 14:18:39 +0100 Subject: [PATCH 3/3] add coverage and a hint to `PYO3_CROSS_LIB_DIR` --- pyo3-build-config/src/impl_.rs | 36 ++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 62c906ce2ec..ff668a33440 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -1276,7 +1276,12 @@ fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result> { /// is not set via `PYO3_CROSS_LIB_DIR`. pub fn find_all_sysconfigdata(cross: &CrossCompileConfig) -> Result> { let sysconfig_paths = if let Some(lib_dir) = cross.lib_dir.as_ref() { - search_lib_dir(lib_dir, cross)? + search_lib_dir(lib_dir, cross).with_context(|| { + format!( + "failed to search the lib dir at 'PYO3_CROSS_LIB_DIR={}'", + lib_dir.display() + ) + })? } else { return Ok(Vec::new()); }; @@ -1329,9 +1334,12 @@ fn is_cpython_lib_dir(path: &str, v: &Option) -> bool { /// recursive search for _sysconfigdata, returns all possibilities of sysconfigdata paths fn search_lib_dir(path: impl AsRef, cross: &CrossCompileConfig) -> Result> { let mut sysconfig_paths = vec![]; - for f in fs::read_dir(path) - .context("failed to search the lib dir, please check your path and permissions.")? - { + for f in fs::read_dir(path.as_ref()).with_context(|| { + format!( + "failed to list the entries in '{}'", + path.as_ref().display() + ) + })? { sysconfig_paths.extend(match &f { // Python 3.7+ sysconfigdata with platform specifics Ok(f) if starts_with(f, "_sysconfigdata_") && ends_with(f, "py") => vec![f.path()], @@ -2757,4 +2765,24 @@ mod tests { ] ); } + + #[test] + fn test_find_sysconfigdata_in_invalid_lib_dir() { + let e = find_all_sysconfigdata(&CrossCompileConfig { + lib_dir: Some(PathBuf::from("/abc/123/not/a/real/path")), + version: None, + implementation: None, + target: triple!("x86_64-unknown-linux-gnu"), + }) + .unwrap_err(); + + // actual error message is platform-dependent, so just check the context we add + assert!(e.report().to_string().starts_with( + "failed to search the lib dir at 'PYO3_CROSS_LIB_DIR=/abc/123/not/a/real/path'\n\ + caused by:\n \ + - 0: failed to list the entries in '/abc/123/not/a/real/path'\n \ + - 1: \ + " + )); + } }