subprocess – Espere de forma asincrónica la salida de un proceso de comunicación

Pregunta:

En primer lugar, un descargo de responsabilidad. He investigado esto muchas veces y estoy bastante seguro de que ya encontré la respuesta de una forma u otra, pero simplemente no la entiendo.

Mi problema es el siguiente:

  • Tengo un proceso que se ejecuta a través de comint
  • Quiero enviar una línea de entrada, capturar la salida y ver cuándo ha terminado (cuando la última línea de la salida coincide con la expresión regular de un mensaje)
  • solo cuando el proceso ha terminado de enviar la salida, quiero enviar otra línea de entrada (por ejemplo).

Para un poco de trasfondo, piense en un modo principal que implemente la interacción con un programa, que puede devolver una cantidad arbitraria de salida, en un tiempo arbitrariamente largo. Esta no debería ser una situación inusual, ¿verdad? De acuerdo, tal vez la parte en la que necesito esperar entre entradas es inusual, pero tiene algunas ventajas sobre el envío de la entrada como un todo:

  • el búfer de salida está muy bien formateado: entrada salida entrada salida …
  • lo que es más importante, cuando se envía una gran cantidad de texto a un proceso, el texto se corta en trozos y los trozos se pegan en el proceso; los puntos de corte son arbitrarios y esto a veces hace que la entrada no sea válida (mi proceso no pegará correctamente un corte de entrada en el medio de un identificador, por ejemplo)

De todos modos, inusual o no, resulta que es complicado. En este momento, estoy usando algo parecido a

(defun mymode--wait-for-output ()
  (let ((buffer (mymode-get-buffer)))
    (with-current-buffer buffer
      (goto-char (point-max))
      (forward-line 0)
      (while (not (mymode-looking-at-prompt))
        (accept-process-output nil 0.001)
        (redisplay)
        (goto-char (point-max))
        (forward-line 0))
      (end-of-line))))

y llamo a esto cada vez después de enviar una línea de entrada y antes de enviar la siguiente. Bueno … funciona, eso ya es algo.

Pero también hace que emacs se cuelgue mientras espera la salida. La razón es obvia, y pensé que si incluía algún tipo de sleep-for asincrónica (por ejemplo, 1 s) en el bucle, retrasaría la salida en 1 s, pero suprimiría el bloqueo. Excepto que parece que este tipo de sleep-for asincrónico no existe .

¿O lo hace? De manera más general, ¿hay alguna forma idiomática de lograr esto con emacs? En otras palabras:

¿Cómo enviar entrada a un proceso, esperar la salida y luego enviar más entrada de forma asincrónica?

Al buscar (ver las preguntas relacionadas), he visto principalmente menciones de centinelas (pero no creo que se aplique en mi caso, ya que el proceso no finaliza) y de algunos ganchos de comint (pero luego, ¿qué debería hacer? hacer que el gancho sea local en el búfer, convertir mi "evaluar las líneas restantes" en una función, agregar esta función al gancho y limpiar el gancho después; eso suena muy sucio, ¿no?).

Lo siento si no estoy siendo claro, o si realmente hay una respuesta obvia disponible en alguna parte, estoy realmente confundido por todas las complejidades de la interacción del proceso.

Si es necesario, puedo hacer de todo esto un ejemplo de trabajo, pero me temo que solo haría una "pregunta de proceso específico con una respuesta de proceso específica" como todas las que encontré antes y no me ayudaron.

Algunas preguntas relacionadas sobre SO:

Respuesta:

En primer lugar, no debería utilizar accept-process-output si desea un procesamiento asincrónico. Emacs aceptará la salida cada vez que esté esperando la entrada del usuario.

La forma correcta de hacerlo es usar funciones de filtro para interceptar la salida. No es necesario que cree o elimine los filtros dependiendo de si todavía tiene líneas para enviar. Más bien, normalmente declarará un solo filtro durante la vida útil del proceso y usará variables locales del búfer para rastrear el estado y hacer diferentes cosas según sea necesario.

La interfaz de bajo nivel

Las funciones de filtro son lo que está buscando. Las funciones de filtro son para dar salida a lo que son los centinelas a la terminación.

(defun mymode--output-filter (process string)
  (let ((buffer (process-buffer process)))
    (when (buffer-live-p buffer)
      (with-current-buffer buffer
        (goto-char (point-max))
        (forward-line 0)
        (when (mymode-looking-at-prompt)
          (do-something)
          (goto-char (point-max)))))))

Mire el manual o muchos ejemplos que vienen con Emacs ( grep para process-filter en los archivos .el ).

Registre su función de filtro con

(set-process-filter 'mymode--output-filter)

La interfaz de comint

Comint define una función de filtro que hace algunas cosas:

  • Cambie al búfer que debería contener la salida del proceso.
  • Ejecute las funciones en la lista comint-preoutput-filter-functions , pasándoles el nuevo texto como argumento.
  • Realice una eliminación ad hoc de comint-prompt-regexp duplicados, basada en comint-prompt-regexp .
  • Inserte la salida del proceso al final del búfer
  • Ejecute las funciones en la lista comint-output-filter-functions , pasándoles el nuevo texto como argumento.

Dado que su modo se basa en comint, debe registrar su filtro en comint-output-filter-functions . Debe configurar comint-prompt-regexp para que coincida con su solicitud. No creo que Comint tenga una función incorporada para detectar un fragmento de salida completo (es decir, entre dos indicaciones), pero puede ayudar. El marcador comint-last-input-end se establece al final del último fragmento de entrada. Tiene un nuevo fragmento de salida cuando el final del último mensaje es después de comint-last-input-end . Cómo encontrar el final del último indicador depende de la versión de Emacs:

  • Hasta 24.3, la superposición comint-last-prompt-overlay abarca la última indicación.
  • Desde 24.4, la variable comint-last-prompt contiene marcadores al inicio y al final de la última solicitud.
(defun mymode--comint-output-filter (string)
  (let ((start (marker-position comint-last-input-end))
        (end (if (boundp 'comint-last-prompt-overlay)
                 (and comint-last-prompt-overlay (overlay-start comint-last-prompt-overlay))
               (and comint-last-prompt (cdr comint-last-prompt))))
  (when (and start end (< start end))
    (let ((new-output-chunk (buffer-substring-no-properties start end)))
      ...)))

Es posible que desee agregar protecciones en caso de que el proceso emita una salida en una secuencia que no sea {recibir entrada, emitir salida, mostrar mensaje}.

Leave a Comment

Your email address will not be published.

Scroll to Top

istanbul avukat

-

web tasarım