diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 96213d46d..6677c7777 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -78,6 +78,8 @@ jobs: if: runner.os == 'macOS' - name: Cache cargo dependencies uses: actions/cache@v2 + # cache gets corrupted for some reason on mac + if: runner.os != 'macOS' with: path: | ~/.cargo/registry @@ -108,12 +110,20 @@ jobs: RUSTPYTHONPATH: ${{ github.workspace }}/Lib if: runner.os == 'Linux' - name: run cpython tests (macOS lightweight) - run: target/release/rustpython -m test -x test_argparse -x test_json -x test_bytes -x test_long -v + run: + target/release/rustpython -m test -v -x + test_argparse test_json test_bytes test_bytearray test_long test_unicode test_array + test_asyncgen test_list test_complex test_json test_set test_dis test_calendar env: RUSTPYTHONPATH: ${{ github.workspace }}/Lib if: runner.os == 'macOS' - name: run cpython tests (windows partial - fixme) - run: target/release/rustpython -m test -x test_argparse -x test_json -x test_bytes -x test_long -x test_pwd -x test_bool -x test_cgi -x test_complex -x test_exception_hierarchy -x test_glob -x test_importlib -x test_iter -x test_list -x test_os -x test_pathlib -x test_py_compile -x test_set -x test_shutil -x test_sys -x test_unicode -x test_unittest -x test_venv -x test_zipimport -v + run: + target/release/rustpython -m test -v -x + test_argparse test_json test_bytes test_long test_pwd test_bool test_cgi test_complex + test_exception_hierarchy test_glob test_iter test_list test_os test_pathlib + test_py_compile test_set test_shutil test_sys test_unicode test_unittest test_venv + test_zipimport test_importlib env: RUSTPYTHONPATH: ${{ github.workspace }}/Lib if: runner.os == 'Windows' diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 952cd9492..da8166d40 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -16,7 +16,8 @@ from urllib.parse import quote_from_bytes as urlquote_from_bytes supports_symlinks = True if os.name == 'nt': import nt - if sys.getwindowsversion()[:2] >= (6, 0): + # XXX RUSTPYTHON TODO: nt._getfinalpathname + if False and sys.getwindowsversion()[:2] >= (6, 0): from nt import _getfinalpathname else: supports_symlinks = False @@ -42,7 +43,10 @@ _IGNORED_WINERRORS = ( ) def _ignore_error(exception): - return (getattr(exception, 'errno', None) in _IGNORED_ERROS or + # XXX RUSTPYTHON: added check for FileNotFoundError, file.exists() on windows throws it + # but with a errno==ESRCH for some reason + return (isinstance(exception, FileNotFoundError) or + getattr(exception, 'errno', None) in _IGNORED_ERROS or getattr(exception, 'winerror', None) in _IGNORED_WINERRORS) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 6c634ccd8..f12e8bbbd 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1066,12 +1066,15 @@ def temp_dir(path=None, quiet=False): try: rmtree(path) except OSError as exc: - # XXX RUSTPYTHON: added quiet check here, not sure why rmtree fails on windows - if not quiet: - raise - warnings.warn(f'unable to remove temporary' - f'directory {path!r}: {exc}', - RuntimeWarning, stacklevel=3) + # XXX RUSTPYTHON: something something async file removal? + # also part of the thing with rmtree() + # throwing PermissionError, I think + if os.path.exists(path): + if not quiet: + raise + warnings.warn(f'unable to remove temporary' + f'directory {path!r}: {exc}', + RuntimeWarning, stacklevel=3) @contextlib.contextmanager def change_cwd(path, quiet=False): diff --git a/Lib/test/test_importlib/test_locks.py b/Lib/test/test_importlib/test_locks.py index db494c339..b63655838 100644 --- a/Lib/test/test_importlib/test_locks.py +++ b/Lib/test/test_importlib/test_locks.py @@ -5,6 +5,7 @@ init = test_util.import_importlib('importlib') import sys import threading import weakref +import unittest from test import support from test import lock_tests @@ -37,6 +38,7 @@ LOCK_TYPES = {kind: splitinit._bootstrap._ModuleLock LockType=LOCK_TYPES) +@unittest.skipIf(sys.platform == "darwin", "TODO: RUSTPYTHON") class DeadlockAvoidanceTests: def setUp(self): diff --git a/Lib/test/test_importlib/test_windows.py b/Lib/test/test_importlib/test_windows.py index 005b685cc..08709d431 100644 --- a/Lib/test/test_importlib/test_windows.py +++ b/Lib/test/test_importlib/test_windows.py @@ -88,6 +88,8 @@ class WindowsRegistryFinderTests: @unittest.skipUnless(sys.platform.startswith('win'), 'requires Windows') class WindowsExtensionSuffixTests: + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_tagged_suffix(self): suffixes = self.machinery.EXTENSION_SUFFIXES expected_tag = ".cp{0.major}{0.minor}-{1}.pyd".format(sys.version_info, diff --git a/extra_tests/snippets/stdlib_os.py b/extra_tests/snippets/stdlib_os.py index f96192b75..def45bfaa 100644 --- a/extra_tests/snippets/stdlib_os.py +++ b/extra_tests/snippets/stdlib_os.py @@ -17,7 +17,8 @@ assert_raises(FileNotFoundError, assert_raises(FileNotFoundError, lambda: os.rename('DOES_NOT_EXIST', 'DOES_NOT_EXIST 2')) -if hasattr(os, "sendfile"): +# sendfile only supports in_fd as non-socket on linux and solaris +if hasattr(os, "sendfile") and sys.platform.startswith("linux"): src_fd = os.open('README.md', os.O_RDONLY) dest_fd = os.open('destination.md', os.O_RDWR | os.O_CREAT) src_len = os.stat('README.md').st_size diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 25af3381a..7a6dae6b0 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -437,7 +437,17 @@ mod _os { #[pyfunction] fn remove(path: PyPathLike, dir_fd: DirFd, vm: &VirtualMachine) -> PyResult<()> { let path = make_path(vm, &path, &dir_fd)?; - fs::remove_file(path).map_err(|err| err.into_pyexception(vm)) + let is_junction = cfg!(windows) + && fs::symlink_metadata(path).map_or(false, |meta| { + let ty = meta.file_type(); + ty.is_dir() && ty.is_symlink() + }); + let res = if is_junction { + fs::remove_dir(path) + } else { + fs::remove_file(path) + }; + res.map_err(|err| err.into_pyexception(vm)) } #[pyfunction] @@ -963,7 +973,12 @@ impl<'a> SupportFunc { where F: IntoPyNativeFunc, { - let func_obj = vm.ctx.new_function(func); + let ctx = &vm.ctx; + let func_obj = ctx + .new_function_named(func, name.to_owned()) + .into_function() + .with_module(ctx.new_str(MODULE_NAME)) + .build(ctx); Self { name, func_obj, diff --git a/vm/src/stdlib/winreg.rs b/vm/src/stdlib/winreg.rs index f04ed7697..f869fb633 100644 --- a/vm/src/stdlib/winreg.rs +++ b/vm/src/stdlib/winreg.rs @@ -3,13 +3,13 @@ use crate::builtins::pystr::PyStrRef; use crate::builtins::pytype::PyTypeRef; use crate::common::lock::{PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard}; use crate::exceptions::IntoPyException; -use crate::function::OptionalArg; use crate::pyobject::{ BorrowValue, PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue, StaticType, TryFromObject, }; use crate::VirtualMachine; use std::convert::TryInto; +use std::ffi::OsStr; use std::io; use winapi::shared::winerror; use winreg::{enums::RegType, RegKey, RegValue}; @@ -98,25 +98,43 @@ impl Hkey { } } } + fn into_key(self) -> RegKey { + let k = match self { + Self::PyHKEY(py) => py.key().raw_handle(), + Self::Constant(k) => k, + }; + RegKey::predef(k) + } } -fn winreg_OpenKey( +#[derive(FromArgs)] +struct OpenKeyArgs { + #[pyarg(any)] key: Hkey, - subkey: Option, - reserved: OptionalArg, - access: OptionalArg, - vm: &VirtualMachine, -) -> PyResult { - let reserved = reserved.unwrap_or(0); - let access = access.unwrap_or(winreg::enums::KEY_READ); + #[pyarg(any)] + sub_key: Option, + #[pyarg(any, default = "0")] + reserved: i32, + #[pyarg(any, default = "winreg::enums::KEY_READ")] + access: u32, +} + +fn winreg_OpenKey(args: OpenKeyArgs, vm: &VirtualMachine) -> PyResult { + let OpenKeyArgs { + key, + sub_key, + reserved, + access, + } = args; + if reserved != 0 { // RegKey::open_subkey* doesn't have a reserved param, so this'll do return Err(vm.new_value_error("reserved param must be 0".to_owned())); } - let subkey = subkey.as_ref().map_or("", |s| s.borrow_value()); + let sub_key = sub_key.as_ref().map_or("", |s| s.borrow_value()); let key = key - .with_key(|k| k.open_subkey_with_flags(subkey, access)) + .with_key(|k| k.open_subkey_with_flags(sub_key, access)) .map_err(|e| e.into_pyexception(vm))?; Ok(PyHKEY::new(key)) @@ -177,6 +195,39 @@ fn winreg_CloseKey(key: Hkey) { } } +fn winreg_CreateKey(key: Hkey, subkey: Option, vm: &VirtualMachine) -> PyResult { + let k = match subkey { + Some(subkey) => { + let (k, _disp) = key + .with_key(|k| k.create_subkey(&*subkey.borrow_value())) + .map_err(|e| e.into_pyexception(vm))?; + k + } + None => key.into_key(), + }; + Ok(PyHKEY::new(k)) +} + +fn winreg_SetValue( + key: Hkey, + subkey: Option, + typ: u32, + value: PyStrRef, + vm: &VirtualMachine, +) -> PyResult<()> { + if typ != winreg::enums::REG_SZ as u32 { + return Err(vm.new_type_error("type must be winreg.REG_SZ".to_owned())); + } + let subkey = subkey.as_ref().map_or("", |s| s.borrow_value()); + key.with_key(|k| k.set_value(subkey, &OsStr::new(value.borrow_value()))) + .map_err(|e| e.into_pyexception(vm)) +} + +fn winreg_DeleteKey(key: Hkey, subkey: PyStrRef, vm: &VirtualMachine) -> PyResult<()> { + key.with_key(|k| k.delete_subkey(subkey.borrow_value())) + .map_err(|e| e.into_pyexception(vm)) +} + fn reg_to_py(value: RegValue, vm: &VirtualMachine) -> PyResult { macro_rules! bytes_to_int { ($int:ident, $f:ident, $name:ident) => {{ @@ -255,6 +306,9 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef { "EnumKey" => named_function!(ctx, winreg, EnumKey), "EnumValue" => named_function!(ctx, winreg, EnumValue), "CloseKey" => named_function!(ctx, winreg, CloseKey), + "CreateKey" => named_function!(ctx, winreg, CreateKey), + "SetValue" => named_function!(ctx, winreg, SetValue), + "DeleteKey" => named_function!(ctx, winreg, DeleteKey), }); macro_rules! add_constants { diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 8720ac17e..654e7fef8 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -703,7 +703,7 @@ settrace() -- set the global debug tracing function #[cfg(windows)] { - let _getwindowsversion = WindowsVersion::make_class(ctx); + WindowsVersion::make_class(ctx); extend_module!(vm, module, { "getwindowsversion" => named_function!(ctx, sys, getwindowsversion), })