diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index bcb7e498e..b7b4fa6f8 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -2076,6 +2076,7 @@ class TestCopyFile(unittest.TestCase): self.assertRaises(OSError, shutil.copyfile, 'srcfile', 'destfile') + @unittest.skip("TODO: RUSTPYTHON, panics with 'no blocks left to pop'") @unittest.skipIf(MACOS, "skipped on macOS") def test_w_dest_open_fails(self): @@ -2096,6 +2097,7 @@ class TestCopyFile(unittest.TestCase): self.assertEqual(srcfile._exited_with[1].args, ('Cannot open "destfile"',)) + @unittest.skip("TODO: RUSTPYTHON, panics with 'no blocks left to pop'") @unittest.skipIf(MACOS, "skipped on macOS") def test_w_dest_close_fails(self): diff --git a/derive/src/from_args.rs b/derive/src/from_args.rs index 42559fba2..6afb653de 100644 --- a/derive/src/from_args.rs +++ b/derive/src/from_args.rs @@ -12,6 +12,7 @@ enum ParameterKind { PositionalOnly, PositionalOrKeyword, KeywordOnly, + Flatten, } impl ParameterKind { @@ -20,6 +21,7 @@ impl ParameterKind { "positional_only" => Some(ParameterKind::PositionalOnly), "positional_or_keyword" => Some(ParameterKind::PositionalOrKeyword), "keyword_only" => Some(ParameterKind::KeywordOnly), + "flatten" => Some(ParameterKind::Flatten), _ => None, } } @@ -52,7 +54,7 @@ impl ArgAttribute { err_span!( first_arg, "The first argument to #[pyarg()] must be the parameter type, either \ - 'positional_only', 'positional_or_keyword', or 'keyword_only'." + 'positional_only', 'positional_or_keyword', 'keyword_only', or 'flatten'." ) })?; @@ -78,6 +80,9 @@ impl ArgAttribute { } fn parse_argument(&mut self, arg: &NestedMeta) -> Result<(), Diagnostic> { + if let ParameterKind::Flatten = self.kind { + bail_span!(arg, "can't put additional arguments on a flatten arg") + } match arg { NestedMeta::Meta(Meta::Path(path)) => { if path_eq(&path, "default") { @@ -154,6 +159,11 @@ fn generate_field(field: &Field) -> Result { }); } } + if let ParameterKind::Flatten = attr.kind { + return Ok(quote! { + #name: ::rustpython_vm::function::FromArgs::from_args(vm, args)?, + }); + } let middle = quote! { .map(|x| ::rustpython_vm::pyobject::TryFromObject::try_from_object(vm, x)).transpose()? }; @@ -174,6 +184,7 @@ fn generate_field(field: &Field) -> Result { ParameterKind::KeywordOnly => quote! { ::rustpython_vm::function::ArgumentError::RequiredKeywordArgument(tringify!(#name)) }, + ParameterKind::Flatten => unreachable!(), }; quote! { .ok_or_else(|| #err)? @@ -196,6 +207,7 @@ fn generate_field(field: &Field) -> Result { #name: args.take_keyword(stringify!(#name))#middle#ending, } } + ParameterKind::Flatten => unreachable!(), }; Ok(file_output) } diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 4eb33c3f2..500ecada0 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -1476,6 +1476,20 @@ fn os_link(src: PyPathLike, dst: PyPathLike, vm: &VirtualMachine) -> PyResult<() fs::hard_link(src.path, dst.path).map_err(|err| convert_io_error(vm, err)) } +#[derive(FromArgs)] +struct UtimeArgs { + #[pyarg(positional_or_keyword)] + path: PyPathLike, + #[pyarg(positional_or_keyword, default = "None")] + times: Option, + #[pyarg(keyword_only, default = "None")] + ns: Option, + #[pyarg(flatten)] + dir_fd: DirFd, + #[pyarg(flatten)] + follow_symlinks: FollowSymlinks, +} + fn os_utime( _path: PyPathLike, _time: OptionalArg,