【问题标题】:Override function value with asynchronous call用异步调用覆盖函数值
【发布时间】:2016-12-06 05:13:45
【问题描述】:

如何在异步调用期间使用cl-letf 或类似方法覆盖符号的函数值?我想在调用start-processstart-process-shell-command 后停止显示缓冲区,而是取回一个字符串。

这是一个简化的示例,其中绑定display-buffer 适用于同步版本,但不适用于异步版本。另外,我已将 lexical-binding 设置为 true。

(defun tst-fun-sync (url)
  (call-process "wget" nil "*wget*" nil url "-O" "-")
  (with-current-buffer "*wget*"
    (display-buffer (current-buffer))))

(defun tst-fun-async (url)
  (set-process-sentinel
   (start-process "wget" "*wget*" "wget" url "-O" "-")
   #'(lambda (p _m)
       (when (zerop (process-exit-status p))
         (with-current-buffer (process-buffer p)
           (display-buffer (current-buffer)))))))

(defun tst-fun-no-display (fun &rest args)
  (cl-letf (((symbol-function 'display-buffer)
             #'(lambda (&rest _ignored)
                 (message "%s" (buffer-string)))))
    (apply fun args)))

;; The first has desired result, but not the second
;; (tst-fun-no-display 'tst-fun-sync "http://www.stackoverflow.com")
;; (tst-fun-no-display 'tst-fun-async "http://www.stackoverflow.com")

【问题讨论】:

  • 使用shell-command-to-string怎么样?例如,(replace-regexp-in-string "\n" "" (shell-command-to-string "date")) 不需要显示缓冲区来使用它——也就是说,在我可以看到的示例中,不需要使用 display-buffer
  • 您可能还对能够以字符串形式捕获所有进程输出的进程过滤器感兴趣。 gnu.org/software/emacs/manual/html_node/elisp/…

标签: asynchronous emacs elisp lexical-scope


【解决方案1】:

让我们定义一个临时重新绑定set-process-sentinel 的宏,以便可以用包装函数修饰标记函数。

(defmacro with-sentinel-wrapper (wrapper-fn &rest body)
  (let ((sps (gensym))
        (proc (gensym))
        (fn (gensym)))
    `(let ((,sps (symbol-function 'set-process-sentinel)))
       (cl-letf (((symbol-function 'set-process-sentinel)
                  (lambda (,proc ,fn)
                    (funcall ,sps ,proc (funcall ,wrapper-fn ,fn)))))
                ,@body))))

包装器可以通过建立任何有用的动态绑定来更改调用哨兵的动态上下文。在这里,我重用您的 cl-letf 来更改显示的作用:

(with-sentinel-wrapper (lambda (fn)
                        (lexical-let ((fun fn)) 
                          (lambda (p m)
                            (cl-letf (((symbol-function 'display-buffer)
                                       #'(lambda (&rest _ignored)
                                           (message "%s" (buffer-string)))))
                              (funcall fun p m)))))
  (tst-fun-async "http://www.stackoverflow.com"))

现在,如果您不确定异步进程是否真的调用了set-process-sentinel,您可能需要破解其他函数。

【讨论】:

  • 谢谢,但这似乎与我的尝试具有相同的结果 - 对异步版本没有影响。我认为它不会起作用,因为 unwind-protect 在异步调用完成之前会执行 bodyforms
  • @jenesaisquoi 谢谢,我现在明白了。我改变了答案。不完美,但它有效。
  • 非常感谢!所以我猜词法环境在常规的 set-process-sentinel 调用中被扔掉了?我希望我能更好地理解这一点。我最初曾尝试破解哨兵,但无法让它发挥作用。幸运的是,我所针对的功能确实使用了 set-process-sentinel 所以这是完美的
  • @jenesaisquoi 谢谢。 cl-letf,除非另有说明,emacs 中的大多数绑定都是动态的:它们像调用堆栈一样来来去去。因此,当您退出调用进程创建的函数时,任何有效的绑定都将消失。稍后,当调用哨兵时,代码将使用全局绑定运行。我在这里所做的是确保在调用哨兵时,我们的自定义哨兵(lambda (p m) ...)会更改调用实际哨兵fun的动态上下文(注意我在这里使用lexical-let来存储未来使用的实际哨兵功能)。
  • 对,我知道 elisp 默认为动态范围,并且在我的脚本顶部使用 ;; -*- lexical-binding: t-*-,因此使用 let 代替 lexical-let 也可以。我原以为闭包会包裹哨兵-我想知道使用process-put 将函数附加到进程是否是另一种选择。再次感谢,非常有帮助:)
猜你喜欢
  • 1970-01-01
  • 2010-10-25
  • 2013-07-28
  • 1970-01-01
  • 1970-01-01
  • 2012-07-25
  • 1970-01-01
  • 2013-03-28
  • 2018-12-13
相关资源
最近更新 更多