Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 43 additions & 40 deletions src/core/IronPython.Modules/nt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1144,7 +1144,7 @@ private static Process MakeProcess() {

private static object SpawnProcessImpl(CodeContext/*!*/ context, Process process, int mode, string path, object? args, [CallerMemberName] string? methodname = null) {
try {
process.StartInfo.Arguments = ArgumentsToString(context, args, methodname);
SetArguments(process.StartInfo, ArgumentsToList(context, args, methodname));
process.StartInfo.FileName = path;
process.StartInfo.UseShellExecute = false;
} catch (Exception e) {
Expand Down Expand Up @@ -1179,6 +1179,48 @@ private static object SpawnProcessImpl(CodeContext/*!*/ context, Process process
_processToIdMapping[id] = process;
return ScriptingRuntimeHelpers.Int32ToObject(id);
}

// Set the process arguments from the list of args
static void SetArguments(ProcessStartInfo startInfo, List<string> args) {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
startInfo.Arguments = string.Join(" ", args);
return;
}

#if NET
foreach (var arg in args) {
startInfo.ArgumentList.Add(arg);
}
#else
startInfo.Arguments = string.Join(" ", args.Select(QuoteIfNeeded));

static string QuoteIfNeeded(string str) {
if (str.Contains(' ')) {
return "\"" + str.Replace("\"", "\"\"") + "\"";
}
return str;
}
#endif
}

static List<string> ArgumentsToList(CodeContext/*!*/ context, object? args, string? methodname) {
IEnumerator? argsEnumerator;
if (!PythonOps.TryGetEnumerator(context, args, out argsEnumerator)) {
throw PythonOps.TypeErrorForBadInstance("args parameter must be sequence, not {0}", args);
}

var list = new List<string>();
try {
// skip the first element, which is the name of the command being run
argsEnumerator.MoveNext();
while (argsEnumerator.MoveNext()) {
list.Add(ConvertToFsString(context, argsEnumerator.Current, "elements of 'args'", methodname));
}
} finally {
(argsEnumerator as IDisposable)?.Dispose();
}
return list;
}
}

/// <summary>
Expand All @@ -1201,45 +1243,6 @@ private static void SetEnvironment(CodeContext context, System.Collections.Speci
}
#endif

/// <summary>
/// Convert a sequence of args to a string suitable for using to spawn a process.
/// </summary>
private static string ArgumentsToString(CodeContext/*!*/ context, object? args, string? methodname) {
IEnumerator? argsEnumerator;
StringBuilder? sb = null;
if (!PythonOps.TryGetEnumerator(context, args, out argsEnumerator)) {
throw PythonOps.TypeErrorForBadInstance("args parameter must be sequence, not {0}", args);
}

bool space = false;
try {
// skip the first element, which is the name of the command being run
argsEnumerator.MoveNext();
while (argsEnumerator.MoveNext()) {
if (sb == null) sb = new StringBuilder(); // lazy creation
string strarg = ConvertToFsString(context, argsEnumerator.Current, "elements of 'args'", methodname);
if (space) {
sb.Append(' ');
}
if (strarg.Contains(' ')) {
sb.Append('"');
// double quote any existing quotes
sb.Append(strarg.Replace("\"", "\"\""));
sb.Append('"');
} else {
sb.Append(strarg);
}
space = true;
}
} finally {
IDisposable? disposable = argsEnumerator as IDisposable;
if (disposable != null) disposable.Dispose();
}

if (sb == null) return "";
return sb.ToString();
}

#if FEATURE_PROCESS
[SupportedOSPlatform("windows"), PythonHidden(PlatformsAttribute.PlatformFamily.Unix)]
public static void startfile([NotNone] string filepath, string operation = "open") {
Expand Down
1 change: 0 additions & 1 deletion tests/suite/modules/system_related/test_nt.py
Original file line number Diff line number Diff line change
Expand Up @@ -902,7 +902,6 @@ def test__getfullpathname_neg(self):
for bad in [None, 0, 34, -big(12345), 3.14, object, self.test__getfullpathname]:
self.assertRaises(TypeError, nt._getfullpathname, bad)

@unittest.skipIf(is_netcoreapp, 'TODO: figure out')
@unittest.skipUnless(sys.platform == "win32", 'windir is Windows specific')
def test_cp15514(self):
cmd_variation_list = ['%s -c "print(__name__)"' % sys.executable,
Expand Down
4 changes: 2 additions & 2 deletions tests/suite/test_stdconsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ def setUp(self):
# "lastline" : valuestring is compared against the last line of the output
# "regexp" : valuestring is a regular expression compared against the entire output
def TestCommandLine(self, args, expected_output, expected_exitcode = 0):
if not is_cli:
# https://github.com/IronLanguages/ironpython3/issues/648
if os.name == 'nt':
# must self-quote arguments with spawnv on Windows
try:
idx = args.index("-c")
if idx + 1 < len(args):
Expand Down
Loading