diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index df283451f..3409d44ad 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -256,8 +256,6 @@ class TestNtpath(NtpathTestCase): tester("ntpath.realpath('\\'.join(['..'] * 50))", ntpath.splitdrive(expected)[0] + '\\') - # TODO: RUSTPYTHON - @unittest.expectedFailure @support.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_basic(self): @@ -271,8 +269,6 @@ class TestNtpath(NtpathTestCase): self.assertPathEqual(ntpath.realpath(os.fsencode(ABSTFN + "1")), os.fsencode(ABSTFN)) - # TODO: RUSTPYTHON - @unittest.expectedFailure @support.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_relative(self): @@ -343,8 +339,7 @@ class TestNtpath(NtpathTestCase): self.assertPathEqual(ntpath.realpath(b"broken5"), os.fsencode(ABSTFN + r"\missing")) - # TODO: RUSTPYTHON - @unittest.expectedFailure + @unittest.skip("TODO: RUSTPYTHON, leaves behind TESTFN") @support.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_symlink_loops(self): @@ -391,8 +386,6 @@ class TestNtpath(NtpathTestCase): # Test using relative path as well. self.assertPathEqual(ntpath.realpath(ntpath.basename(ABSTFN)), ABSTFN) - # TODO: RUSTPYTHON - @unittest.expectedFailure @support.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_symlink_prefix(self): @@ -427,8 +420,6 @@ class TestNtpath(NtpathTestCase): self.assertPathEqual(ntpath.realpath("\\\\?\\" + ABSTFN + "3.link"), "\\\\?\\" + ABSTFN + "3.") - # TODO: RUSTPYTHON - @unittest.expectedFailure @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') def test_realpath_nul(self): tester("ntpath.realpath('NUL')", r'\\.\NUL') @@ -546,8 +537,6 @@ class TestNtpath(NtpathTestCase): tester('ntpath.expanduser("~test")', 'C:\\eric\\test') tester('ntpath.expanduser("~")', 'C:\\eric\\idle') - # TODO: RUSTPYTHON - @unittest.expectedFailure @unittest.skipUnless(nt, "abspath requires 'nt' module") def test_abspath(self): tester('ntpath.abspath("C:\\")', "C:\\") @@ -707,8 +696,6 @@ class TestNtpath(NtpathTestCase): """Assert that two strings are equal ignoring case differences.""" self.assertEqual(s1.lower(), s2.lower()) - # TODO: RUSTPYTHON - @unittest.expectedFailure @unittest.skipUnless(nt, "OS helpers require 'nt' module") def test_nt_helpers(self): # Trivial validation that the helpers do not break, and support both @@ -743,29 +730,11 @@ class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase): pathmodule = ntpath attributes = ['relpath'] - # TODO: RUSTPYTHON - if sys.platform == "win32": - @unittest.expectedFailure - def test_exists(self): - super().test_exists() - # TODO: RUSTPYTHON @unittest.expectedFailure def test_expandvars_nonascii(self): super().test_expandvars_nonascii() - # TODO: RUSTPYTHON - if sys.platform == "win32": - @unittest.expectedFailure - def test_isdir(self): - super().test_isdir() - - # TODO: RUSTPYTHON - if sys.platform == "win32": - @unittest.expectedFailure - def test_isfile(self): - super().test_isfile() - # TODO: RUSTPYTHON if sys.platform == "linux": @unittest.expectedFailure diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 9ae76fab4..b117f1016 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -678,12 +678,18 @@ impl ExceptionZoo { "__str__" => ctx.new_method("__str__", key_error_str), }); + let errno_getter = ctx.new_readonly_getset("errno", |exc: PyBaseExceptionRef| { + let args = exc.args(); + let args = args.borrow_value(); + args.get(0).filter(|_| args.len() > 1).cloned() + }); + #[cfg(windows)] extend_class!(ctx, &excs.os_error, { - "errno" => ctx.new_readonly_getset("errno", |exc: PyBaseExceptionRef| { - let args = exc.args(); - let args = args.borrow_value(); - args.get(0).filter(|_| args.len() > 1).cloned() - }), + // TODO: this isn't really accurate + "winerror" => errno_getter.clone(), + }); + extend_class!(ctx, &excs.os_error, { + "errno" => errno_getter, }); extend_class!(ctx, &excs.unicode_decode_error, { diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index e403a2f26..2b8f8ff2e 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -560,10 +560,8 @@ mod _os { fn remove(path: PyPathLike, dir_fd: DirFd<0>, vm: &VirtualMachine) -> PyResult<()> { let [] = dir_fd.0; let is_junction = cfg!(windows) - && fs::symlink_metadata(&path).map_or(false, |meta| { - let ty = meta.file_type(); - ty.is_dir() && ty.is_symlink() - }); + && fs::metadata(&path).map_or(false, |meta| meta.file_type().is_dir()) + && fs::symlink_metadata(&path).map_or(false, |meta| meta.file_type().is_symlink()); let res = if is_junction { fs::remove_dir(&path) } else { @@ -3014,6 +3012,95 @@ mod nt { } } + #[pyfunction] + fn _getfinalpathname(path: PyPathLike, vm: &VirtualMachine) -> PyResult { + let real = path + .as_ref() + .canonicalize() + .map_err(|e| e.into_pyexception(vm))?; + path.mode.process_path(real, vm) + } + + #[pyfunction] + fn _getfullpathname(path: PyPathLike, vm: &VirtualMachine) -> PyResult { + let wpath = path.to_widecstring(vm)?; + let mut buffer = vec![0u16; winapi::shared::minwindef::MAX_PATH]; + let ret = unsafe { + winapi::um::fileapi::GetFullPathNameW( + wpath.as_ptr(), + buffer.len() as _, + buffer.as_mut_ptr(), + std::ptr::null_mut(), + ) + }; + if ret == 0 { + return Err(errno_err(vm)); + } + if ret as usize > buffer.len() { + buffer.resize(ret as usize, 0); + let ret = unsafe { + winapi::um::fileapi::GetFullPathNameW( + wpath.as_ptr(), + buffer.len() as _, + buffer.as_mut_ptr(), + std::ptr::null_mut(), + ) + }; + if ret == 0 { + return Err(errno_err(vm)); + } + } + let buffer = widestring::WideCString::from_vec_with_nul(buffer).unwrap(); + path.mode.process_path(buffer.to_os_string(), vm) + } + + #[pyfunction] + fn _getvolumepathname(path: PyPathLike, vm: &VirtualMachine) -> PyResult { + let wide = path.to_widecstring(vm)?; + let buflen = std::cmp::max(wide.len(), winapi::shared::minwindef::MAX_PATH); + let mut buffer = vec![0u16; buflen]; + let ret = unsafe { + winapi::um::fileapi::GetVolumePathNameW(wide.as_ptr(), buffer.as_mut_ptr(), buflen as _) + }; + if ret == 0 { + return Err(errno_err(vm)); + } + let buffer = widestring::WideCString::from_vec_with_nul(buffer).unwrap(); + path.mode.process_path(buffer.to_os_string(), vm) + } + + #[pyfunction] + fn _getdiskusage(path: PyPathLike, vm: &VirtualMachine) -> PyResult<(u64, u64)> { + use winapi::shared::{ntdef::ULARGE_INTEGER, winerror}; + use winapi::um::fileapi::GetDiskFreeSpaceExW; + let wpath = path.to_widecstring(vm)?; + let mut _free_to_me = ULARGE_INTEGER::default(); + let mut total = ULARGE_INTEGER::default(); + let mut free = ULARGE_INTEGER::default(); + let ret = + unsafe { GetDiskFreeSpaceExW(wpath.as_ptr(), &mut _free_to_me, &mut total, &mut free) }; + if ret != 0 { + return unsafe { Ok((*total.QuadPart(), *free.QuadPart())) }; + } + let err = io::Error::last_os_error(); + if err.raw_os_error() == Some(winerror::ERROR_DIRECTORY as i32) { + if let Some(parent) = path.as_ref().parent() { + let parent = widestring::WideCString::from_os_str(parent).unwrap(); + + let ret = unsafe { + GetDiskFreeSpaceExW(parent.as_ptr(), &mut _free_to_me, &mut total, &mut free) + }; + + if ret == 0 { + return Err(errno_err(vm)); + } else { + return unsafe { Ok((*total.QuadPart(), *free.QuadPart())) }; + } + } + } + return Err(err.into_pyexception(vm)); + } + pub(super) fn support_funcs() -> Vec { Vec::new() } diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 4cdc6d8ad..44ab2df4b 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -642,7 +642,9 @@ settrace() -- set the global debug tracing function ); let modules = ctx.new_dict(); - let prefix = option_env!("RUSTPYTHON_PREFIX").unwrap_or("/usr/local"); + // TODO: the windows one doesn't really make sense + let default_prefix = if cfg!(windows) { "C:\\" } else { "/usr/local" }; + let prefix = option_env!("RUSTPYTHON_PREFIX").unwrap_or(default_prefix); let base_prefix = option_env!("RUSTPYTHON_BASEPREFIX").unwrap_or(prefix); let exec_prefix = option_env!("RUSTPYTHON_EXECPREFIX").unwrap_or(prefix); let base_exec_prefix = option_env!("RUSTPYTHON_BASEEXECPREFIX").unwrap_or(exec_prefix);