Skip to content

Commit 97d76a9

Browse files
authored
Merge pull request RustPython#7141 from youknowone/execve
Accept any mapping for os.execve env argument
2 parents 1f127da + 7351c04 commit 97d76a9

5 files changed

Lines changed: 39 additions & 18 deletions

File tree

Lib/test/_test_multiprocessing.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4440,6 +4440,7 @@ def test_shared_memory_SharedMemoryServer_ignores_sigint(self):
44404440

44414441
smm.shutdown()
44424442

4443+
@unittest.skip("TODO: RUSTPYTHON: sem_unlink cleanup race causes spurious stderr output")
44434444
@unittest.skipIf(os.name != "posix", "resource_tracker is posix only")
44444445
@resource_tracker_format_subtests
44454446
def test_shared_memory_SharedMemoryManager_reuses_resource_tracker(self):

Lib/test/test_httpservers.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -994,7 +994,6 @@ def test_url_collapse_path(self):
994994
msg='path = %r\nGot: %r\nWanted: %r' %
995995
(path, actual, expected))
996996

997-
@unittest.expectedFailureIf(sys.platform != 'win32', 'TODO: RUSTPYTHON; AssertionError: Tuples differ: (b"", None, 200) != (b"Hello World\n", "text/html", <HTTPStatus.OK: 200>)')
998997
def test_headers_and_content(self):
999998
res = self.request('/cgi-bin/file1.py')
1000999
self.assertEqual(
@@ -1005,7 +1004,6 @@ def test_issue19435(self):
10051004
res = self.request('///////////nocgi.py/../cgi-bin/nothere.sh')
10061005
self.assertEqual(res.status, HTTPStatus.NOT_FOUND)
10071006

1008-
@unittest.expectedFailureIf(sys.platform != 'win32', 'TODO: RUSTPYTHON; b"" != b"1, python, 123456\n"')
10091007
def test_post(self):
10101008
params = urllib.parse.urlencode(
10111009
{'spam' : 1, 'eggs' : 'python', 'bacon' : 123456})
@@ -1014,7 +1012,6 @@ def test_post(self):
10141012

10151013
self.assertEqual(res.read(), b'1, python, 123456' + self.linesep)
10161014

1017-
@unittest.expectedFailureIf(sys.platform != 'win32', 'TODO: RUSTPYTHON; AssertionError: b"" != b"32768 32768\n"')
10181015
def test_large_content_length(self):
10191016
for w in range(15, 25):
10201017
size = 1 << w
@@ -1023,7 +1020,6 @@ def test_large_content_length(self):
10231020
res = self.request('/cgi-bin/file7.py', 'POST', body, headers)
10241021
self.assertEqual(res.read(), b'%d %d' % (size, size) + self.linesep)
10251022

1026-
@unittest.expectedFailureIf(sys.platform != 'win32', 'TODO: RUSTPYTHON; AssertionError: b"" != b"Hello World\n"')
10271023
def test_large_content_length_truncated(self):
10281024
with support.swap_attr(self.request_handler, 'timeout', 0.001):
10291025
for w in range(18, 65):
@@ -1037,7 +1033,6 @@ def test_invaliduri(self):
10371033
res.read()
10381034
self.assertEqual(res.status, HTTPStatus.NOT_FOUND)
10391035

1040-
@unittest.expectedFailureIf(sys.platform != 'win32', 'TODO: RUSTPYTHON; AssertionError: Tuples differ: (b"Hello World\n", "text/html", <HTTPStatus.OK: 200>) != (b"", None, 200)')
10411036
def test_authorization(self):
10421037
headers = {b'Authorization' : b'Basic ' +
10431038
base64.b64encode(b'username:pass')}
@@ -1046,15 +1041,13 @@ def test_authorization(self):
10461041
(b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK),
10471042
(res.read(), res.getheader('Content-type'), res.status))
10481043

1049-
@unittest.expectedFailureIf(sys.platform != 'win32', 'TODO: RUSTPYTHON; AssertionError: Tuples differ: (b"Hello World\n", "text/html", <HTTPStatus.OK: 200>) != (b"", None, 200)')
10501044
def test_no_leading_slash(self):
10511045
# http://bugs.python.org/issue2254
10521046
res = self.request('cgi-bin/file1.py')
10531047
self.assertEqual(
10541048
(b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK),
10551049
(res.read(), res.getheader('Content-type'), res.status))
10561050

1057-
@unittest.expectedFailureIf(sys.platform != 'win32', 'TODO: RUSTPYTHON; ValueError: signal only works in main thread')
10581051
def test_os_environ_is_not_altered(self):
10591052
signature = "Test CGI Server"
10601053
os.environ['SERVER_SOFTWARE'] = signature
@@ -1064,36 +1057,31 @@ def test_os_environ_is_not_altered(self):
10641057
(res.read(), res.getheader('Content-type'), res.status))
10651058
self.assertEqual(os.environ['SERVER_SOFTWARE'], signature)
10661059

1067-
@unittest.expectedFailureIf(sys.platform != 'win32', 'TODO: RUSTPYTHON; ValueError: signal only works in main thread')
10681060
def test_urlquote_decoding_in_cgi_check(self):
10691061
res = self.request('/cgi-bin%2ffile1.py')
10701062
self.assertEqual(
10711063
(b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK),
10721064
(res.read(), res.getheader('Content-type'), res.status))
10731065

1074-
@unittest.expectedFailureIf(sys.platform != 'win32', 'TODO: RUSTPYTHON; AssertionError: Tuples differ: (b"Hello World\n", "text/html", <HTTPStatus.OK: 200>) != (b"", None, 200)')
10751066
def test_nested_cgi_path_issue21323(self):
10761067
res = self.request('/cgi-bin/child-dir/file3.py')
10771068
self.assertEqual(
10781069
(b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK),
10791070
(res.read(), res.getheader('Content-type'), res.status))
10801071

1081-
@unittest.expectedFailureIf(sys.platform != 'win32', 'TODO: RUSTPYTHON; ValueError: signal only works in main thread')
10821072
def test_query_with_multiple_question_mark(self):
10831073
res = self.request('/cgi-bin/file4.py?a=b?c=d')
10841074
self.assertEqual(
10851075
(b'a=b?c=d' + self.linesep, 'text/html', HTTPStatus.OK),
10861076
(res.read(), res.getheader('Content-type'), res.status))
10871077

1088-
@unittest.expectedFailureIf(sys.platform != 'win32', 'TODO: RUSTPYTHON; AssertionError: Tuples differ: (b"k=aa%2F%2Fbb&//q//p//=//a//b//\n", "text/html", <HTTPStatus.OK: 200>) != (b"", None, 200)')
10891078
def test_query_with_continuous_slashes(self):
10901079
res = self.request('/cgi-bin/file4.py?k=aa%2F%2Fbb&//q//p//=//a//b//')
10911080
self.assertEqual(
10921081
(b'k=aa%2F%2Fbb&//q//p//=//a//b//' + self.linesep,
10931082
'text/html', HTTPStatus.OK),
10941083
(res.read(), res.getheader('Content-type'), res.status))
10951084

1096-
@unittest.expectedFailureIf(sys.platform != 'win32', 'TODO: RUSTPYTHON; Tuples differ: (b"", None, 200) != (b"Hello World\n", "text/html", <HTTPStatus.OK: 200>)')
10971085
def test_cgi_path_in_sub_directories(self):
10981086
try:
10991087
CGIHTTPRequestHandler.cgi_directories.append('/sub/dir/cgi-bin')
@@ -1104,7 +1092,6 @@ def test_cgi_path_in_sub_directories(self):
11041092
finally:
11051093
CGIHTTPRequestHandler.cgi_directories.remove('/sub/dir/cgi-bin')
11061094

1107-
@unittest.expectedFailureIf(sys.platform != 'win32', 'TODO: RUSTPYTHON; AssertionError: b"HTTP_ACCEPT=text/html,text/plain" not found in b""')
11081095
def test_accept(self):
11091096
browser_accept = \
11101097
'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'

Lib/test/test_threading.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,7 @@ def test_main_thread_after_fork(self):
745745
"main ident True\n"
746746
"current is main True\n")
747747

748+
@unittest.skip("TODO: RUSTPYTHON flaky; process timeout after fork")
748749
@skip_unless_reliable_fork
749750
@unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()")
750751
def test_main_thread_after_fork_from_nonmain_thread(self):

crates/vm/src/stdlib/nt.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ pub use module::raw_set_handle_inheritable;
66
#[pymodule(name = "nt", with(super::os::_os))]
77
pub(crate) mod module {
88
use crate::{
9-
Py, PyResult, TryFromObject, VirtualMachine,
9+
Py, PyObjectRef, PyResult, TryFromObject, VirtualMachine,
1010
builtins::{PyBaseExceptionRef, PyDictRef, PyListRef, PyStrRef, PyTupleRef},
1111
common::{crt_fd, suppress_iph, windows::ToWideString},
1212
convert::ToPyException,
1313
exceptions::OSErrorBuilder,
14-
function::{Either, OptionalArg},
14+
function::{ArgMapping, Either, OptionalArg},
1515
ospath::{OsPath, OsPathOrFd},
1616
stdlib::os::{_os, DirFd, SupportFunc, TargetIsDirectory},
1717
};
@@ -1212,12 +1212,27 @@ pub(crate) mod module {
12121212
}
12131213
}
12141214

1215+
fn envobj_to_dict(env: ArgMapping, vm: &VirtualMachine) -> PyResult<PyDictRef> {
1216+
let obj = env.obj();
1217+
if let Some(dict) = obj.downcast_ref_if_exact::<crate::builtins::PyDict>(vm) {
1218+
return Ok(dict.to_owned());
1219+
}
1220+
let keys = vm.call_method(obj, "keys", ())?;
1221+
let dict = vm.ctx.new_dict();
1222+
for key in keys.get_iter(vm)?.into_iter::<PyObjectRef>(vm)? {
1223+
let key = key?;
1224+
let val = obj.get_item(&*key, vm)?;
1225+
dict.set_item(&*key, val, vm)?;
1226+
}
1227+
Ok(dict)
1228+
}
1229+
12151230
#[cfg(target_env = "msvc")]
12161231
#[pyfunction]
12171232
fn execve(
12181233
path: OsPath,
12191234
argv: Either<PyListRef, PyTupleRef>,
1220-
env: PyDictRef,
1235+
env: ArgMapping,
12211236
vm: &VirtualMachine,
12221237
) -> PyResult<()> {
12231238
use core::iter::once;
@@ -1246,6 +1261,7 @@ pub(crate) mod module {
12461261
.chain(once(core::ptr::null()))
12471262
.collect();
12481263

1264+
let env = envobj_to_dict(env, vm)?;
12491265
// Build environment strings as "KEY=VALUE\0" wide strings
12501266
let mut env_strings: Vec<widestring::WideCString> = Vec::new();
12511267
for (key, value) in env.into_iter() {

crates/vm/src/stdlib/posix.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub mod module {
3131
builtins::{PyDictRef, PyInt, PyListRef, PyStr, PyTupleRef},
3232
convert::{IntoPyException, ToPyObject, TryFromObject},
3333
exceptions::OSErrorBuilder,
34-
function::{Either, KwArgs, OptionalArg},
34+
function::{ArgMapping, Either, KwArgs, OptionalArg},
3535
ospath::{OsPath, OsPathOrFd},
3636
stdlib::os::{
3737
_os, DirFd, FollowSymlinks, SupportFunc, TargetIsDirectory, fs_metadata,
@@ -1073,11 +1073,26 @@ pub mod module {
10731073
.map_err(|err| err.into_pyexception(vm))
10741074
}
10751075

1076+
fn envobj_to_dict(env: ArgMapping, vm: &VirtualMachine) -> PyResult<PyDictRef> {
1077+
let obj = env.obj();
1078+
if let Some(dict) = obj.downcast_ref_if_exact::<crate::builtins::PyDict>(vm) {
1079+
return Ok(dict.to_owned());
1080+
}
1081+
let keys = vm.call_method(obj, "keys", ())?;
1082+
let dict = vm.ctx.new_dict();
1083+
for key in keys.get_iter(vm)?.into_iter::<PyObjectRef>(vm)? {
1084+
let key = key?;
1085+
let val = obj.get_item(&*key, vm)?;
1086+
dict.set_item(&*key, val, vm)?;
1087+
}
1088+
Ok(dict)
1089+
}
1090+
10761091
#[pyfunction]
10771092
fn execve(
10781093
path: OsPath,
10791094
argv: Either<PyListRef, PyTupleRef>,
1080-
env: PyDictRef,
1095+
env: ArgMapping,
10811096
vm: &VirtualMachine,
10821097
) -> PyResult<()> {
10831098
let path = path.into_cstring(vm)?;
@@ -1095,6 +1110,7 @@ pub mod module {
10951110
return Err(vm.new_value_error("execve() arg 2 first element cannot be empty"));
10961111
}
10971112

1113+
let env = envobj_to_dict(env, vm)?;
10981114
let env = env
10991115
.into_iter()
11001116
.map(|(k, v)| -> PyResult<_> {

0 commit comments

Comments
 (0)