[Большая часть этого ответа не интересна, потому что спрашивающий спросил о Python, когда выяснилось, что они имели в виду Пролог, поэтому я потратил впустую свое время на решение проблемы, которую они сказали, а не той, которая у них была на самом деле. Я оставляю это здесь на случай, если что-то из этого будет полезно кому-либо еще.]
Я не думаю, что это проблема с SBCL. Учитывая следующий код:
(defun call-with-process (f program args &rest keys &key &allow-other-keys)
(let ((process (apply #'sb-ext:run-program program args keys)))
(unwind-protect
(funcall f process)
(sb-ext:process-close process))))
(defmacro with-process ((process program args &rest keys &key &allow-other-keys)
&body forms)
`(call-with-process
(lambda (,process)
,@forms)
,program ,args ,@keys))
(defun test-echo-process (&rest strings-to-send)
(with-process (p "/bin/cat" '()
:wait nil
:input ':stream
:output ':stream)
(let ((i (sb-ext:process-input p))
(o (sb-ext:process-output p)))
(dolist (s strings-to-send)
(format i "~A~%" s)
(finish-output i)
(format t "Sent ~A, got ~A~%" s (read-line o))))))
Тогда test-echo-process
отлично работает.
Но эта функция (эквивалентная вашей) зависает:
(defun test-python-process ()
(with-process (p "/usr/bin/python" '()
:wait nil
:input ':stream
:output ':stream)
(let ((i (sb-ext:process-input p))
(o (sb-ext:process-output p)))
(format i "print 'here'~%")
(finish-output i)
(format t "~A~%" (read-line o)))))
Итак, на самом деле проблема в том, как ведет себя Python. И вы можете продемонстрировать это на самом деле. Вот некоторый вывод из терминала:
$ cat | python
print "hi"
print "there"
hi
there
$
Что не отображается, так это то, что после того, как я набрал вторую команду print
, я отправил EOF (т.е. ^ D в Unix).
Таким образом, Python, я думаю, вполне разумно буферизует свой ввод, вероятно, только в том случае, если это не терминал.
Так что вам нужно что-то сделать, чтобы это не произошло. Сначала я поместил программу, которую вы хотите, чтобы Python запускал, в файл, чтобы стандартный ввод выполнял только одно. Но тогда вы окажетесь в мире боли.
Если вы реализуете эту функцию
(defun test-python-script (args &rest strings-to-send)
(with-process (p "/usr/bin/python" args
:wait nil
:input ':stream
:output ':stream)
(let ((i (sb-ext:process-input p))
(o (sb-ext:process-output p)))
(dolist (s strings-to-send)
(format i "~A~%" s)
(finish-output i)
(format t "sent ~A, got ~A~%" s (read-line o))))))
Тогда вы можете подумать, что немного Python в echo.py
выглядит так:
from sys import stdin, exit
if __name__ == '__main__':
for l in stdin:
print "got " + l.strip()
exit(0)
, а затем запуск (test-python-script '("echo.py") "foo" "bar")
будет работать. Но вы были бы неправы по крайней мере двумя способами (вы можете проверить это, просто запустив python echo.py
в командной строке и увидев, что он все еще буферизируется.
Первый способ, которым вы ошибаетесь, заключается в том, что использование файлов в качестве итераторов в Python имеет встроенную буферизацию, которую вы, похоже, не сможете избежать. Вы можете справиться с этим, написав небуферизованный итератор, поэтому echo.py
теперь
from sys import stdin, exit
class UnbufferedLineIterator(object):
# I take back what I said about 'perfectly reasonably'
def __init__(self, stream):
self.stream = stream
def __iter__(self):
return self
def next(self):
line = self.stream.readline()
if len(line) > 0:
return line
else:
raise StopIteration
if __name__ == '__main__':
for l in UnbufferedLineIterator(stdin):
print "got " + l.strip()
exit(0)
И это может сработать, но это не сработает, потому что все еще буферизует где-то на стороне Python. Вы можете избавиться от этого очень грубо, запустив Python с аргументом -u
. Итак, наконец
* (test-python-script '("-u" "echo.py") "foo" "bar")
sent foo, got got foo
sent bar, got got bar
Однако я думаю, что реальный ответ - пойти и спросить людей Python, как это должно работать, потому что я не могу поверить, что -u
правильный ответ или что это может быть так сложно.