subprocess.py works ok

parent 288305e2
......@@ -25,18 +25,34 @@
(define msg "")
(define fds_to_close '())
(catch #t
(lambda ()
(if (> p2cwrite -1)
(if (> p2cwrite 2)
(close-fdes p2cwrite))
(if (> p2cwrite -1)
(if (> c2pread 2)
(close-fdes c2pread))
(if (> errread -1)
(if (> errread 2)
(close-fdes errread))
(if (> errpipe_read -1)
(if (> errpipe_read 2)
(close-fdes errpipe_read))
(when (= c2pwrite 0)
(set! c2pwrite (dup c2pwrite)))
(when (> p2cread 0)
(dup2 p2cread 0)
(set_inheritable 0 #f))
(when (> c2pwrite 1)
(dup2 c2pwrite 1)
(set_inheritable 1 #f))
(if (= c2pwrite 0)
(set! c2pwrite (dup c2pwrite)))
(if errwrite
(when (> errwrite 2)
(dup2 errwrite 2)
(set_inheritable 2 #f)))
(let lp ()
(if errwrite
......@@ -44,22 +60,12 @@
(set! errwrite (dup errwrite))
(lp))))
(if (> p2cread 0)
(dup2 p2cread 0))
(if (> c2pwrite 1)
(dup2 c2pwrite 1))
(if errwrite
(if (> errwrite 2)
(dup2 errwrite 2)))
(if (> p2cread 2)
(close-fdes p2cread))
(if (and c2pwrite (> c2pwrite 2) (not (= c2pwrite p2cread)))
(close-fdes c2pwrite))
(if (and errwrite (> errwrite 2) (not (= errwrite p2cread))
(not (= errwrite c2pwrite)))
(close-fdes errwrite))
......@@ -77,48 +83,48 @@
(if (bool closed_fds)
(for ((fd : fds_to_close)) ()
(close fd)))
(close-fdes fd)))
(let ((argv (to-list argv))
(envp (if (bool envp) (to-list envp) envp)))
(if (and p2cread (>= p2cread 0))
(set-current-input-port p2cread))
(if (and c2pwrite (>= c2pwrite 0))
(set-current-output-port c2pwrite))
(if (and errwrite (>= errwrite 0))
(set-current-error-port errwrite))
(for ((e : exec_array)) ((ep #f))
(try
(lambda ()
(if (bool envp)
(apply execle e (append argv (list envp)))
(apply execl e argv)))
(#:except #t =>
(lambda x
(if (not execmsg)
(set! execmsg ""))
(set! execmsg
(+ execmsg ((@ (guile) format) #f " exec error: ~a~%" x))))))
(#:except
#t
=>
(lambda x
(if (not execmsg)
(set! execmsg ""))
(set! execmsg
(+ execmsg ((@ (guile) format) #f " exec error: ~a~%" x))))))
(let ((er (errno)))
(if (and (not (= er ENOENT)) (not (= er ENOTDIR)) (not ep))
er
ep))
#:final
(if ep (set_errno ep) (set_errno 0))))
(if errwrite
(write errpipe_write errwrite))
(if execmsg
(write errpipe_write execmsg))
(write errpipe_write execmsg))
(if (errno)
(write errpipe_write ((@ (guile) format) #f "exec failed with errno ~a" (errno)))))
(write errpipe_write ((@ (guile) format) #f
"exec failed with errno ~a" (errno)))))
(lambda x
(values)))
(primitive-exit 1))
......@@ -139,33 +145,36 @@
(if (not (isinstance fd int))
(raise (ValueError "bad values(s) in fds_to_keep"))))
(set_blocking errpipe_read #f)
(set_blocking errpipe_write #f)
(set_blocking errpipe_read #f)
(set_blocking errpipe_write #f)
(let ((pid (primitive-fork)))
(if (= pid 0)
(child_exec executable_list
process_args
env_list
cwd
p2cread
p2cwrite
c2pread
c2pwrite
errread
errwrite
errpipe_read
errpipe_write
close_fds
restore_signals
call_setsid
fds_to_keep
preexec_fn)
(begin
;; Child process
(child_exec executable_list
process_args
env_list
cwd
p2cread
p2cwrite
c2pread
c2pwrite
errread
errwrite
errpipe_read
errpipe_write
close_fds
restore_signals
call_setsid
fds_to_keep
preexec_fn))
(begin
#;(if (> c2pwrite 2)
(close c2pwrite))
#;(if (> errwrite 2)
(close errwrite))
#;(if (> p2cread 2)
(close p2cread))
pid))))
......@@ -22,6 +22,23 @@
BufferedRandom TextIOBase TextIOWrapper
StringIO))
(define-syntax-rule (ca code)
(catch #t
(lambda () code)
(lambda x
(match x
(('system-error x _ _ (9))
#f)
(('system-error x _ _ (10))
(raise (ChildProcessError "No child process")))
(('system-error x _ _ (17))
(raise (FileExistsError x)))
(('system-error x _ _ (2))
(raise (FileNotFoundError x)))
(x (raise error x))))))
(define-syntax-rule (aif it p x y) (let ((it p)) (if it x y)))
(define-syntax-rule (ca code)
......@@ -198,10 +215,13 @@
port)))
(define close
(lambda (self)
(check (self port it)
(close-port port)
(set it 'closed #t))))
(lambda (self)
(catch #t
(lambda ()
(check (self port it)
(close-port port)
(set it 'closed #t)))
(lambda x #t))))
(define __enter__
(lambda (self)
......@@ -311,25 +331,25 @@
(define-python-class RawIOBase (IOBase)
(define __next__
(lambda (self)
(let ((x (read self 1)))
(let ((x (ca (read self 1))))
(if (= (len x) 0)
StopIteration
x))))
(define read
(lam (self (= size -1))
(check (self port)
(bytes
(if (< size 0)
((ref self 'readall))
(wrap (get-bytevector-n port size)))))))
(wrap (ca (get-bytevector-n port size))))))))
(define readall
(lambda (self)
(check (self port)
(bytes
(wrap (get-bytevector-all port))))))
(wrap (ca (get-bytevector-all port)))))))
(define readinto
(lambda (self b)
......@@ -377,10 +397,11 @@
(if (pair? name)
(set self '_port (car name))
(set self '_port
(open- (path-it name)
(ca
(open- (path-it name)
#:mode mode
#:closefd closefd
#:opener opener)))
#:opener opener))))
(set self 'mode mode)
(set self 'name (cdr name)))))))
......@@ -394,8 +415,9 @@
(set self '_port port)
(set self '_gtbv get-bytevector)))
(set self '_port
(open-bytevector-input-port
(scm-bytevector initial_bytes))))))
(ca
(open-bytevector-input-port
(scm-bytevector initial_bytes)))))))
(define getvalue
(lambda (self)
......@@ -419,7 +441,7 @@
(lambda* (self #:optional (size 1))
(check (self port)
(list->string
(let ((r (peek-char port)))
(let ((r (ca (peek-char port))))
(if (char? r)
(list r)
(list))))))))
......@@ -427,27 +449,29 @@
(define-python-class BufferedWriter (BufferedIOBase)
(define __init__
(lambda* (self raw #:optional (buffer_size DEFAULT_BUFFER_SIZE))
(let ((port (ref raw '_port)))
(case buffer_size
((0)
(setvbuf port 'none))
((1)
(setvbuf port 'line))
(else
(setvbuf port 'block buffer_size))))
(ca
(let ((port (ref raw '_port)))
(case buffer_size
((0)
(setvbuf port 'none))
((1)
(setvbuf port 'line))
(else
(setvbuf port 'block buffer_size)))))
(set self 'raw raw))))
(define-python-class BufferedRandom (BufferedIOBase)
(define __init__
(lambda* (self raw #:optional (buffer_size DEFAULT_BUFFER_SIZE))
(let ((port (ref raw '_port)))
(case buffer_size
((0)
(setvbuf port 'none))
((1)
(setvbuf port 'line))
(else
(setvbuf port 'block buffer_size))))
(ca
(let ((port (ref raw '_port)))
(case buffer_size
((0)
(setvbuf port 'none))
((1)
(setvbuf port 'line))
(else
(setvbuf port 'block buffer_size)))))
(set self 'raw raw)))
(define peek
......@@ -464,20 +488,21 @@
(define read
(lam (self (= size -1))
(check (self port)
(if (< size 0)
(wraps (get-string-all port))
(wraps (get-string-n port size))))))
(check (self port)
(ca
(if (< size 0)
(wraps (get-string-all port))
(wraps (get-string-n port size)))))))
(define readline
(lam (self (= size -1))
(check (self port)
(wraps (read-line port 'concat)))))
(wraps (ca (read-line port 'concat))))))
(define write
(lambda (self s)
(check (self port)
(put-string port (scm-str s) 0 (len s))
(ca (put-string port (scm-str s) 0 (len s)))
(len s)))))
(define (get-port x)
......@@ -496,21 +521,22 @@
(= line_buffering #f)
(= write_through #f))
(set self 'raw buffer)
(let* ((port (get-port buffer))
(errors (if (bool errors)
(scm-str errors)
(let ((s (port-conversion-strategy port)))
(cond
((eq? s 'error) "strict")
((eq? s 'substitute) "replace")
((eq? s 'escape) "basckslashreplace")))))
(encoding (if (eq? encoding None)
(port-encoding port)
encoding)))
(ca
(let* ((port (get-port buffer))
(errors (if (bool errors)
(scm-str errors)
(let ((s (port-conversion-strategy port)))
(cond
((eq? s 'error) "strict")
((eq? s 'substitute) "replace")
((eq? s 'escape) "basckslashreplace")))))
(encoding (if (eq? encoding None)
(port-encoding port)
encoding)))
;; encoding
(set self 'encoding encoding)
(set-port-encoding! port encoding)
(set self 'encoding encoding)
(set-port-encoding! port encoding)
;; buffering
(if line_buffering
(setvbuf port 'line))
......@@ -530,15 +556,16 @@
(set-port-conversion-strategy! port 'escape)))
;; write trough
(set self 'write_through write_through)))))
(set self 'write_through write_through))))))
(define-python-class StringIO (TextIOBase)
(define __init__
(lam (self (= initial_value "") (= newline "\n"))
(set self 'newline newline)
(if (string-null? initial_value)
(set self '_port (open-output-string))
(set self '_port (open-input-string initial_value)))))
(ca
(if (string-null? initial_value)
(set self '_port (open-output-string))
(set self '_port (open-input-string initial_value))))))
(define getvalue
(lambda (self)
......
......@@ -28,7 +28,7 @@
#:use-module (language python module resource)
#:use-module (language python list)
#:replace (getcwd getuid getenv stat environ)
#:replace (getcwd getuid getenv stat environ dup dup2)
#:export (error name ctermid environb chdir fchdir
fsencode fdencode fspath PathLike getenvb
get_exec_path getgid getegid geteuid fdopen
......@@ -42,7 +42,7 @@
curdir pardir sep extsep altsep pathsep linesep defpath
devnull path
dopen close closerange device_encoding dup dup2 fchmod fchown
dopen close closerange device_encoding fchmod fchown
fdatasync fpathconf fstat fstatvfs fsynch ftruncate isatty
F_LOCK F_TLOCK F_ULOCK F_TEST lockf
SEEK_SET SEEK_CUR SEEK_END SEEK_DATA SEEK_HOLE lseek
......@@ -148,17 +148,22 @@
(raise (FileNotFoundError x)))
(x (raise error x))))))
(define-syntax-rule (capid code)
(define-syntax-rule (caclose code)
(catch #t
(lambda () code)
(lambda x
(match x
(('system-error x _ _ (9))
(values))
(('system-error x _ _ (10))
(raise (ChildProcessError "No child process")))
(('system-error x _ _ (17))
(raise (FileExistsError x)))
(('system-error x _ _ (2))
(raise (FileNotFoundError x)))
(x (raise error x))))))
(define-syntax-rule (rm code)
(let ((r (ca code)))
(if (< r 0)
......@@ -474,7 +479,7 @@
(define close
(lambda (fd)
(ca ((@ (guile) close) fd))))
(caclose ((@ (guile) close) fd))))
(define (closerange fd_low fd_high)
(for ((i : (range fd_low fd_high))) ()
......@@ -633,16 +638,12 @@
(define fdopen builtin:open)
(define pipe
(lambda ()
(let ((x (ca ((@ (guile) pipe)))))
(values (port->fdes (car x)) (port->fdes (cdr x))))))
(define pipe2 #f)
(defineu pipe2 ()
(let ((f (pointer->procedure int
(dynamic-func "pipe2" (dynamic-link))
(list int '* int))))
(list '* int))))
(lambda (flags)
(let* ((a (make-bytevector 8))
(ap (bytevector->pointer a)))
......@@ -650,6 +651,10 @@
(values (bytevector-s32-ref a 0 (native-endianness))
(bytevector-s32-ref a 4 (native-endianness)))))))
(define pipe
(lambda ()
(pipe2 0)))
(define posix_fallocate #f)
(defineu posix_fallocate (2)
......@@ -731,6 +736,9 @@
(define F_GETFL 3)
(define F_SETFL 4)
(define F_GETFD 1)
(define F_SETFD 2)
(defineu fcntl2 () (pointer->procedure int
(dynamic-func "fcntl" (dynamic-link))
......@@ -769,14 +777,15 @@
(define (writev fd buffers) (error "not implemented"))
(define FD_CLOEXEC 1)
(define (set_inheritable fd is-inh?)
(let ((o (rm (fcntl2 fd F_GETFL))))
(let ((o (rm (fcntl2 fd F_GETFD))))
(if is-inh?
(rm (fcntl3 fd F_SETFL (logior o O_CLOEXEC)))
(rm (fcntl3 fd F_SETFL (logand o (lognot O_CLOEXEC)))))))
(rm (fcntl3 fd F_SETFD (logior o FD_CLOEXEC)))
(rm (fcntl3 fd F_SETFD (logand o (lognot FD_CLOEXEC)))))))
(define (get_inheritable fd)
(if (= (logand O_CLOEXEC (rm (fcntl2 fd F_GETFL))) 0)
(if (= (logand FD_CLOEXEC (rm (fcntl2 fd F_GETFD))) 0)
#f
#t))
......
......@@ -7,6 +7,7 @@
#:use-module ((oop pf-objects) #:select (define-python-class))
#:use-module ((language python format2) #:select ())
#:re-export (memoryview)
#:replace (close)
#:export (ClassMethod StaticMethod Funcobj *main* __name__))
(define-syntax re-export-all
......@@ -35,6 +36,16 @@
(set! (@ (language python module os) path)
(Module '(path os module python language) '(path os)))
(define (close fd)
(cond
((integer? fd)
(close-fdes fd))
((port fd)
(close fd))
(else
((ref fd 'close)))))
(re-export-all (language python module _python))
(set! (@@ (language python eval) MM) (@@ (language python compile) void))
......
......@@ -351,7 +351,6 @@ def run(*popenargs, input=None, timeout=None, check=False, **kwargs):
with Popen(*popenargs, **kwargs) as process:
try:
pk('com',process)
stdout, stderr = process.communicate(input, timeout=timeout)
except TimeoutExpired:
process.kill()
......@@ -366,6 +365,7 @@ def run(*popenargs, input=None, timeout=None, check=False, **kwargs):
if check and retcode:
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
return CompletedProcess(process.args, retcode, stdout, stderr)
......@@ -608,25 +608,19 @@ class Popen(object):
try:
if p2cwrite != -1:
print(1)
self.stdin = io.open(p2cwrite, 'wb', bufsize)
if text_mode:
print(2)
self.stdin = io.TextIOWrapper(self.stdin, write_through=True,
line_buffering=(bufsize == 1),
encoding=encoding, errors=errors)
if c2pread != -1:
print(3)
self.stdout = io.open(c2pread, 'rb', bufsize)
if text_mode:
print(4)
self.stdout = io.TextIOWrapper(self.stdout,
encoding=encoding, errors=errors)
if errread != -1:
print(5)
self.stderr = io.open(errread, 'rb', bufsize)
if text_mode:
print(6)
self.stderr = io.TextIOWrapper(self.stderr,
encoding=encoding, errors=errors)
......@@ -637,7 +631,8 @@ class Popen(object):
c2pread, c2pwrite,
errread, errwrite,
restore_signals, start_new_session)
except:
except Exception as e:
# Cleanup if the child failed starting.
for f in filter(None, (self.stdin, self.stdout, self.stderr)):
try:
......@@ -673,7 +668,7 @@ class Popen(object):
def __exit__(self, type, value, traceback):
if self.stdout:
self.stdout.close()
if self.stderr:
if self.stderr:
self.stderr.close()
try: # Flushing a BufferedWriter may raise an error
if self.stdin:
......@@ -740,43 +735,38 @@ class Popen(object):
communicate() returns a tuple (stdout, stderr). These will be
bytes or, if self.universal_newlines was True, a string.
"""
pk(1)
if self._communication_started and input:
raise ValueError("Cannot send input after starting communication")
pk(2)
# Optimization: If we are not worried about timeouts, we haven't
# started communicating, and we have one or zero pipes, using select()
# or threads is unnecessary.
if (timeout is None and not self._communication_started and
[self.stdin, self.stdout, self.stderr].count(None) >= 2):
pk(3)
stdout = None
stderr = None
if self.stdin:
pk(4)
self._stdin_write(input)
self._stdin_write(input)
elif self.stdout:
pk(5)
stdout = self.stdout.read()
self.stdout.close()
elif self.stderr:
pk(6)
stderr = self.stderr.read()
self.stderr.close()
pk(7)
self.wait()
else:
pk(4)
if timeout is not None:
endtime = _time() + timeout
else:
endtime = None
pk(5)
try:
stdout, stderr = self._communicate(input, endtime, timeout)
finally:
self._communication_started = True
sts = self.wait(timeout=self._remaining_time(endtime))
return (stdout, stderr)
......@@ -942,12 +932,16 @@ class Popen(object):
# self._devnull is not always defined.
devnull_fd = getattr(self, '_devnull', None)
if p2cread != -1 and p2cwrite != -1 and p2cread != devnull_fd:
pk(1.5)
os.close(p2cread)
if c2pwrite != -1 and c2pread != -1 and c2pwrite != devnull_fd:
os.close(c2pwrite)
if errwrite != -1 and errread != -1 and errwrite != devnull_fd:
os.close(errwrite)
......@@ -960,11 +954,8 @@ class Popen(object):
# Wait for exec to fail or succeed; possibly raising an
# exception (limited in size)
errpipe_data = bytearray()
pk(6,errpipe_read)
while True:
pk('read')
part = os.read(errpipe_read, 50000)
pk(part)
errpipe_data += part
if not part or len(errpipe_data) > 50000:
break
......@@ -975,6 +966,7 @@ class Popen(object):
if errpipe_data:
try:
pid, sts = os.waitpid(self.pid, 0)
if pid == self.pid:
self._handle_exitstatus(sts)
else:
......@@ -1032,7 +1024,6 @@ class Popen(object):
else:
# Should never happen
raise SubprocessError("Unknown child exit status!")
pk('leave')
def _internal_poll(self, _deadstate=None, _waitpid=os.waitpid,
_WNOHANG=os.WNOHANG, _ECHILD=errno.ECHILD):
......@@ -1043,6 +1034,7 @@ class Popen(object):