Skip to content

Commit c4d18e5

Browse files
reswqadavidhewitt
andauthored
Change search_lib_dir's return type to Result (#4043)
* Change `search_lib_dir`'s return type to Result * add changelog * add coverage and a hint to `PYO3_CROSS_LIB_DIR` --------- Co-authored-by: David Hewitt <[email protected]>
1 parent 0b967d4 commit c4d18e5

File tree

2 files changed

+42
-11
lines changed

2 files changed

+42
-11
lines changed

newsfragments/4043.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Don't panic when `PYO3_CROSS_LIB_DIR` is set to a missing path.

pyo3-build-config/src/impl_.rs

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,7 +1206,7 @@ fn ends_with(entry: &DirEntry, pat: &str) -> bool {
12061206
/// Returns `None` if the library directory is not available, and a runtime error
12071207
/// when no or multiple sysconfigdata files are found.
12081208
fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result<Option<PathBuf>> {
1209-
let mut sysconfig_paths = find_all_sysconfigdata(cross);
1209+
let mut sysconfig_paths = find_all_sysconfigdata(cross)?;
12101210
if sysconfig_paths.is_empty() {
12111211
if let Some(lib_dir) = cross.lib_dir.as_ref() {
12121212
bail!("Could not find _sysconfigdata*.py in {}", lib_dir.display());
@@ -1269,11 +1269,16 @@ fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result<Option<PathBuf>> {
12691269
///
12701270
/// Returns an empty vector when the target Python library directory
12711271
/// is not set via `PYO3_CROSS_LIB_DIR`.
1272-
pub fn find_all_sysconfigdata(cross: &CrossCompileConfig) -> Vec<PathBuf> {
1272+
pub fn find_all_sysconfigdata(cross: &CrossCompileConfig) -> Result<Vec<PathBuf>> {
12731273
let sysconfig_paths = if let Some(lib_dir) = cross.lib_dir.as_ref() {
1274-
search_lib_dir(lib_dir, cross)
1274+
search_lib_dir(lib_dir, cross).with_context(|| {
1275+
format!(
1276+
"failed to search the lib dir at 'PYO3_CROSS_LIB_DIR={}'",
1277+
lib_dir.display()
1278+
)
1279+
})?
12751280
} else {
1276-
return Vec::new();
1281+
return Ok(Vec::new());
12771282
};
12781283

12791284
let sysconfig_name = env_var("_PYTHON_SYSCONFIGDATA_NAME");
@@ -1291,7 +1296,7 @@ pub fn find_all_sysconfigdata(cross: &CrossCompileConfig) -> Vec<PathBuf> {
12911296
sysconfig_paths.sort();
12921297
sysconfig_paths.dedup();
12931298

1294-
sysconfig_paths
1299+
Ok(sysconfig_paths)
12951300
}
12961301

12971302
fn is_pypy_lib_dir(path: &str, v: &Option<PythonVersion>) -> bool {
@@ -1322,17 +1327,22 @@ fn is_cpython_lib_dir(path: &str, v: &Option<PythonVersion>) -> bool {
13221327
}
13231328

13241329
/// recursive search for _sysconfigdata, returns all possibilities of sysconfigdata paths
1325-
fn search_lib_dir(path: impl AsRef<Path>, cross: &CrossCompileConfig) -> Vec<PathBuf> {
1330+
fn search_lib_dir(path: impl AsRef<Path>, cross: &CrossCompileConfig) -> Result<Vec<PathBuf>> {
13261331
let mut sysconfig_paths = vec![];
1327-
for f in fs::read_dir(path).expect("Path does not exist") {
1332+
for f in fs::read_dir(path.as_ref()).with_context(|| {
1333+
format!(
1334+
"failed to list the entries in '{}'",
1335+
path.as_ref().display()
1336+
)
1337+
})? {
13281338
sysconfig_paths.extend(match &f {
13291339
// Python 3.7+ sysconfigdata with platform specifics
13301340
Ok(f) if starts_with(f, "_sysconfigdata_") && ends_with(f, "py") => vec![f.path()],
13311341
Ok(f) if f.metadata().map_or(false, |metadata| metadata.is_dir()) => {
13321342
let file_name = f.file_name();
13331343
let file_name = file_name.to_string_lossy();
13341344
if file_name == "build" || file_name == "lib" {
1335-
search_lib_dir(f.path(), cross)
1345+
search_lib_dir(f.path(), cross)?
13361346
} else if file_name.starts_with("lib.") {
13371347
// check if right target os
13381348
if !file_name.contains(&cross.target.operating_system.to_string()) {
@@ -1342,12 +1352,12 @@ fn search_lib_dir(path: impl AsRef<Path>, cross: &CrossCompileConfig) -> Vec<Pat
13421352
if !file_name.contains(&cross.target.architecture.to_string()) {
13431353
continue;
13441354
}
1345-
search_lib_dir(f.path(), cross)
1355+
search_lib_dir(f.path(), cross)?
13461356
} else if is_cpython_lib_dir(&file_name, &cross.version)
13471357
|| is_pypy_lib_dir(&file_name, &cross.version)
13481358
|| is_graalpy_lib_dir(&file_name, &cross.version)
13491359
{
1350-
search_lib_dir(f.path(), cross)
1360+
search_lib_dir(f.path(), cross)?
13511361
} else {
13521362
continue;
13531363
}
@@ -1376,7 +1386,7 @@ fn search_lib_dir(path: impl AsRef<Path>, cross: &CrossCompileConfig) -> Vec<Pat
13761386
}
13771387
}
13781388

1379-
sysconfig_paths
1389+
Ok(sysconfig_paths)
13801390
}
13811391

13821392
/// Find cross compilation information from sysconfigdata file
@@ -2747,4 +2757,24 @@ mod tests {
27472757
]
27482758
);
27492759
}
2760+
2761+
#[test]
2762+
fn test_find_sysconfigdata_in_invalid_lib_dir() {
2763+
let e = find_all_sysconfigdata(&CrossCompileConfig {
2764+
lib_dir: Some(PathBuf::from("/abc/123/not/a/real/path")),
2765+
version: None,
2766+
implementation: None,
2767+
target: triple!("x86_64-unknown-linux-gnu"),
2768+
})
2769+
.unwrap_err();
2770+
2771+
// actual error message is platform-dependent, so just check the context we add
2772+
assert!(e.report().to_string().starts_with(
2773+
"failed to search the lib dir at 'PYO3_CROSS_LIB_DIR=/abc/123/not/a/real/path'\n\
2774+
caused by:\n \
2775+
- 0: failed to list the entries in '/abc/123/not/a/real/path'\n \
2776+
- 1: \
2777+
"
2778+
));
2779+
}
27502780
}

0 commit comments

Comments
 (0)