@@ -345,6 +345,7 @@ class Popen(args, bufsize=-1, executable=None,
345345"""
346346
347347import sys
348+ ironpython = (sys .implementation .name == 'ironpython' )
348349mswindows = (sys .platform == "win32" )
349350
350351import io
@@ -401,6 +402,11 @@ class STARTUPINFO:
401402 hStdOutput = None
402403 hStdError = None
403404 wShowWindow = 0
405+ elif ironpython :
406+ import threading
407+ import clr
408+ clr .AddReference ("System" )
409+ from System .Diagnostics import Process
404410else :
405411 import _posixsubprocess
406412 import select
@@ -457,6 +463,8 @@ def __repr__(self):
457463
458464 __del__ = Close
459465 __str__ = __repr__
466+ elif ironpython :
467+ __all__ .extend (["Process" ])
460468
461469try :
462470 MAXFD = os .sysconf ("SC_OPEN_MAX" )
@@ -778,6 +786,13 @@ def __init__(self, args, bufsize=-1, executable=None,
778786 raise ValueError (
779787 "close_fds is not supported on Windows platforms"
780788 " if you redirect stdin/stdout/stderr" )
789+
790+ elif ironpython :
791+ if preexec_fn is not None :
792+ raise ValueError ("preexec_fn is not supported by IronPython" )
793+ # if close_fds:
794+ # raise ValueError("close_fds is not supported by IronPython")
795+
781796 else :
782797 # POSIX
783798 if close_fds is _PLATFORM_DEFAULT_CLOSE_FDS :
@@ -984,7 +999,6 @@ def _check_timeout(self, endtime, orig_timeout):
984999 if _time () > endtime :
9851000 raise TimeoutExpired (self .args , orig_timeout )
9861001
987-
9881002 if mswindows :
9891003 #
9901004 # Windows methods
@@ -1272,6 +1286,117 @@ def terminate(self):
12721286
12731287 kill = terminate
12741288
1289+
1290+ elif ironpython :
1291+ #
1292+ # Dotnet methods
1293+ #
1294+ def _get_handles (self , stdin , stdout , stderr ):
1295+ # Can't get redirect file before Process.Start() is called
1296+ # postpone it to _execute_child
1297+ return (stdin , - 1 , - 1 , stdout , - 1 , stderr )
1298+
1299+ def _execute_child (self , args , executable , preexec_fn , close_fds ,
1300+ pass_fds , cwd , env ,
1301+ startupinfo , creationflags , shell ,
1302+ stdin , unused_p2cwrite ,
1303+ unused_c2pread , stdout ,
1304+ unused_errread , stderr ,
1305+ unused_restore_signals , unused_start_new_session ):
1306+ """Execute program (Dotnet version)"""
1307+ p = Process ()
1308+ s = p .StartInfo
1309+
1310+ if env :
1311+ for k , v in env .items ():
1312+ s .Environment [k ] = v
1313+
1314+ if shell :
1315+ if not isinstance (args , str ):
1316+ args = list2cmdline (args )
1317+ # escape backslash and double quote
1318+ args = '' .join ('\\ ' + c if c in {'\\ ' , '"' } else c for c in args )
1319+ s .Arguments = '-c "{}"' .format (args )
1320+ s .FileName = executable or '/bin/sh'
1321+ else :
1322+ if not isinstance (args , str ):
1323+ s .FileName = args [0 ]
1324+ s .Arguments = list2cmdline (args [1 :])
1325+ else :
1326+ s .FileName = args
1327+
1328+ s .RedirectStandardInput = stdin is not None
1329+ s .RedirectStandardOutput = stdout is not None
1330+ s .RedirectStandardError = stderr is not None
1331+ s .WorkingDirectory = cwd
1332+ s .UseShellExecute = False
1333+
1334+ p .Start ()
1335+
1336+ self .pid = p .Id
1337+ self ._child_created = True
1338+ self ._handle = p
1339+
1340+ if stdin == PIPE :
1341+ self .stdin = open (p .StandardInput .BaseStream )
1342+ if stdout == PIPE :
1343+ self .stdout = io .BufferedReader (open (p .StandardOutput .BaseStream ))
1344+ if stderr == PIPE :
1345+ self .stderr = io .BufferedReader (open (p .StandardError .BaseStream ))
1346+
1347+ # dotnet can't redirect stdio to file/stream, thus has to feed from parent
1348+ if stdin not in (None , DEVNULL , PIPE ):
1349+ # assume file-like object
1350+ input = stdin .read ()
1351+ with open (self ._handle .StandardInput .BaseStream ) as f :
1352+ f .write (input )
1353+
1354+ def _internal_poll (self ):
1355+ """Check if child process has terminated. Returns returncode
1356+ attribute.
1357+
1358+ This method is called by __del__, so it can only refer to objects
1359+ in its local scope.
1360+
1361+ """
1362+ if self .returncode is None and self ._handle .HasExited :
1363+ self .returncode = self ._handle .ExitCode
1364+ return self .returncode
1365+
1366+ def wait (self , timeout = None , endtime = None ):
1367+ """Wait for child process to terminate. Returns returncode
1368+ attribute."""
1369+ if endtime is not None :
1370+ timeout = self ._remaining_time (endtime )
1371+ if timeout is None :
1372+ self ._handle .WaitForExit ()
1373+ else :
1374+ self ._handle .WaitForExit (int (timeout * 1000 ))
1375+ self .returncode = self ._handle .ExitCode
1376+ return self .returncode
1377+
1378+ def _communicate (self , input , endtime , orig_timeout ):
1379+ # .NET framework caches stdout and stderr
1380+ # so we can simply wait then read
1381+ if self .stdin :
1382+ if input :
1383+ self .stdin .write (input )
1384+ self .stdin .close ()
1385+
1386+ if orig_timeout is not None :
1387+ self .wait (endtime = endtime )
1388+
1389+ return (
1390+ self .stdout .read () if self .stdout else None ,
1391+ self .stderr .read () if self .stderr else None ,
1392+ )
1393+
1394+ def terminate (self ):
1395+ """Terminates the process."""
1396+ self ._handle .Kill ()
1397+
1398+ kill = terminate
1399+
12751400 else :
12761401 #
12771402 # POSIX methods
0 commit comments