0

I'm trying to write advice such that each time I eval a python snippet, it automatically output the exception time in the emacs python shell

(defun python-send-with-time-record (orig-func string &optional process msg)
  (let ((wrap (mapconcat #'identity
                         `("import time"
                           "_EMACS_PYTHON_START_TIME = time.time()"
                           ,string
                           "print('(took: {elapsed:.2f} s)'.format(elapsed=time.time() - _EMACS_PYTHON_START_TIME))")
                         "\n")))
    (funcall orig-func wrap process msg)))
(advice-add 'python-shell-send-string :around #'python-send-with-time-record)

However, when I eval something like (python-shell-send-string "print(123)"), Emacs took me to the debugger

Debugger entered--Lisp error: (error "Variable binding depth exceeds max-specpdl-size")
  window-live-p(#<window 7 on init-python.el>)
  window-normalize-window(#<window 7 on init-python.el> t)
  unrecord-window-buffer(#<window 7 on init-python.el> #<buffer  *temp file*>)
  replace-buffer-in-windows(#<buffer  *temp file*>)
  kill-buffer(#<buffer  *temp file*>)
  #[0 "\301\300!\205    �\302\300!\207" [#<buffer  *temp file*> buffer-name kill-buffer] 2]()
  python-shell--save-temp-file("import time\n_EMACS_PYTHON_START_TIME = time.time()\nimport codecs, os;__pyfile = codecs.open('''/var/folders/rp/fqsg67z15d18xs191qk3xyvw0000gn/T/py8260vvw''', encoding='''utf-8''');__code = __pyfile.read().encode('''utf-8''');__pyfile.close();os.remove('''/var/folders/rp/fqsg67z15d18xs191qk3xyvw0000gn/T/py8260vvw''');exec(compile(__code, '''/Users/user/.emacs.d/init/init-python.el''', 'exec'));\nprint('(took: {elapsed:.2f} s)'.format(elapsed=time.time() - _EMACS_PYTHON_START_TIME))")
  #[769 "\206�\300!\301\302\"\203\"�\303!\304 \206�\211\305\306$\266\202\2029�\307\"\210\301\310\"\2035�\301\311\"\2059�\307\312\"\207" [python-shell-get-process-or-error string-match ".\n+." python-shell--save-temp-file buffer-file-name python-shell-send-file t comint-send-string "\n\\'" "\n[  ].*\n?\\'" "\n"] 11 ("/usr/local/Cellar/emacs-mac/emacs-25.3-mac-6.8/share/emacs/25.3/lisp/progmodes/python.elc" . 86989) (byte-code "\300\301!\302\303E\207" [read-string "Python command: " nil t] 3)]("import time\n_EMACS_PYTHON_START_TIME = time.time()\nimport codecs, os;__pyfile = codecs.open('''/var/folders/rp/fqsg67z15d18xs191qk3xyvw0000gn/T/py8260vvw''', encoding='''utf-8''');__code = __pyfile.read().encode('''utf-8''');__pyfile.close();os.remove('''/var/folders/rp/fqsg67z15d18xs191qk3xyvw0000gn/T/py8260vvw''');exec(compile(__code, '''/Users/user/.emacs.d/init/init-python.el''', 'exec'));\nprint('(took: {elapsed:.2f} s)'.format(elapsed=time.time() - _EMACS_PYTHON_START_TIME))" #<process Python> nil)
  funcall(#[769 "\206�\300!\301\302\"\203\"�\303!\304 \206�\211\305\306$\266\202\2029�\307\"\210\301\310\"\2035�\301\311\"\2059�\307\312\"\207" [python-shell-get-process-or-error string-match ".\n+." python-shell--save-temp-file buffer-file-name python-shell-send-file t comint-send-string "\n\\'" "\n[  ].*\n?\\'" "\n"] 11 ("/usr/local/Cellar/emacs-mac/emacs-25.3-mac-6.8/share/emacs/25.3/lisp/progmodes/python.elc" . 86989) (byte-code "\300\301!\302\303E\207" [read-string "Python command: " nil t] 3)] "import time\n_EMACS_PYTHON_START_TIME = time.time()\nimport codecs, os;__pyfile = codecs.open('''/var/folders/rp/fqsg67z15d18xs191qk3xyvw0000gn/T/py8260vvw''', encoding='''utf-8''');__code = __pyfile.read().encode('''utf-8''');__pyfile.close();os.remove('''/var/folders/rp/fqsg67z15d18xs191qk3xyvw0000gn/T/py8260vvw''');exec(compile(__code, '''/Users/user/.emacs.d/init/init-python.el''', 'exec'));\nprint('(took: {elapsed:.2f} s)'.format(elapsed=time.time() - _EMACS_PYTHON_START_TIME))" #<process Python> nil)
  (let ((wrap (mapconcat (function identity) (cons "import time" (cons "_EMACS_PYTHON_START_TIME = time.time()" (cons string (quote ...)))) "\n"))) (funcall orig-func wrap process msg))
  python-send-with-time-record(#[769 "\206�\300!\301\302\"\203\"�\303!\304 \206�\211\305\306$\266\202\2029�\307\"\210\301\310\"\2035�\301\311\"\2059�\307\312\"\207" [python-shell-get-process-or-error string-match ".\n+." python-shell--save-temp-file buffer-file-name python-shell-send-file t comint-send-string "\n\\'" "\n[     ].*\n?\\'" "\n"] 11 ("/usr/local/Cellar/emacs-mac/emacs-25.3-mac-6.8/share/emacs/25.3/lisp/progmodes/python.elc" . 86989) (byte-code "\300\301!\302\303E\207" [read-string "Python command: " nil t] 3)] "import codecs, os;__pyfile = codecs.open('''/var/folders/rp/fqsg67z15d18xs191qk3xyvw0000gn/T/py8260QPN''', encoding='''utf-8''');__code = __pyfile.read().encode('''utf-8''');__pyfile.close();os.remove('''/var/folders/rp/fqsg67z15d18xs191qk3xyvw0000gn/T/py8260QPN''');exec(compile(__code, '''/Users/user/.emacs.d/init/init-python.el''', 'exec'));" #<process Python>)
  apply(python-send-with-time-record #[769 "\206�\300!\301\302\"\203\"�\303!\304 \206�\211\305\306$\266\202\2029�\307\"\210\301\310\"\2035�\301\311\"\2059�\307\312\"\207" [python-shell-get-process-or-error string-match ".\n+." python-shell--save-temp-file buffer-file-name python-shell-send-file t comint-send-string "\n\\'" "\n[   ].*\n?\\'" "\n"] 11 ("/usr/local/Cellar/emacs-mac/emacs-25.3-mac-6.8/share/emacs/25.3/lisp/progmodes/python.elc" . 86989) (byte-code "\300\301!\302\303E\207" [read-string "Python command: " nil t] 3)] ("import codecs, os;__pyfile = codecs.open('''/var/folders/rp/fqsg67z15d18xs191qk3xyvw0000gn/T/py8260QPN''', encoding='''utf-8''');__code = __pyfile.read().encode('''utf-8''');__pyfile.close();os.remove('''/var/folders/rp/fqsg67z15d18xs191qk3xyvw0000gn/T/py8260QPN''');exec(compile(__code, '''/Users/user/.emacs.d/init/init-python.el''', 'exec'));" #<process Python>))
  python-shell-send-string("import codecs, os;__pyfile = codecs.open('''/var/folders/rp/fqsg67z15d18xs191qk3xyvw0000gn/T/py8260QPN''', encoding='''utf-8''');__code = __pyfile.read().encode('''utf-8''');__pyfile.close();os.remove('''/var/folders/rp/fqsg67z15d18xs191qk3xyvw0000gn/T/py8260QPN''');exec(compile(__code, '''/Users/user/.emacs.d/init/init-python.el''', 'exec'));" #<process Python>)
  python-shell-send-file("/Users/user/.emacs.d/init/init-python.el" #<process Python> "/var/folders/rp/fqsg67z15d18xs191qk3xyvw0000gn/T/py8260QPN" t)
  #[769 "\206�\300!\301\302\"\203\"�\303!\304 \206�\211\305\306$\266\202\2029�\307\"\210\301\310\"\2035�\301\311\"\2059�\307\312\"\207" [python-shell-get-process-or-error string-match ".\n+." python-shell--save-temp-file buffer-file-name python-shell-send-file t comint-send-string "\n\\'" "\n[  ].*\n?\\'" "\n"] 11 ("/usr/local/Cellar/emacs-mac/emacs-25.3-mac-6.8/share/emacs/25.3/lisp/progmodes/python.elc" . 86989) (byte-code "\300\301!\302\303E\207" [read-string "Python command: " nil t] 3)]("import time\n_EMACS_PYTHON_START_TIME = time.time()\nprint(1223)\nprint(42324)\nprint('(took: {elapsed:.2f} s)'.format(elapsed=time.time() - _EMACS_PYTHON_START_TIME))" nil nil)
  funcall(#[769 "\206�\300!\301\302\"\203\"�\303!\304 \206�\211\305\306$\266\202\2029�\307\"\210\301\310\"\2035�\301\311\"\2059�\307\312\"\207" [python-shell-get-process-or-error string-match ".\n+." python-shell--save-temp-file buffer-file-name python-shell-send-file t comint-send-string "\n\\'" "\n[  ].*\n?\\'" "\n"] 11 ("/usr/local/Cellar/emacs-mac/emacs-25.3-mac-6.8/share/emacs/25.3/lisp/progmodes/python.elc" . 86989) (byte-code "\300\301!\302\303E\207" [read-string "Python command: " nil t] 3)] "import time\n_EMACS_PYTHON_START_TIME = time.time()\nprint(1223)\nprint(42324)\nprint('(took: {elapsed:.2f} s)'.format(elapsed=time.time() - _EMACS_PYTHON_START_TIME))" nil nil)
  (let ((wrap (mapconcat (function identity) (cons "import time" (cons "_EMACS_PYTHON_START_TIME = time.time()" (cons string (quote ...)))) "\n"))) (funcall orig-func wrap process msg))
  python-send-with-time-record(#[769 "\206�\300!\301\302\"\203\"�\303!\304 \206�\211\305\306$\266\202\2029�\307\"\210\301\310\"\2035�\301\311\"\2059�\307\312\"\207" [python-shell-get-process-or-error string-match ".\n+." python-shell--save-temp-file buffer-file-name python-shell-send-file t comint-send-string "\n\\'" "\n[     ].*\n?\\'" "\n"] 11 ("/usr/local/Cellar/emacs-mac/emacs-25.3-mac-6.8/share/emacs/25.3/lisp/progmodes/python.elc" . 86989) (byte-code "\300\301!\302\303E\207" [read-string "Python command: " nil t] 3)] "print(1223)\nprint(42324)")
  apply(python-send-with-time-record #[769 "\206�\300!\301\302\"\203\"�\303!\304 \206�\211\305\306$\266\202\2029�\307\"\210\301\310\"\2035�\301\311\"\2059�\307\312\"\207" [python-shell-get-process-or-error string-match ".\n+." python-shell--save-temp-file buffer-file-name python-shell-send-file t comint-send-string "\n\\'" "\n[   ].*\n?\\'" "\n"] 11 ("/usr/local/Cellar/emacs-mac/emacs-25.3-mac-6.8/share/emacs/25.3/lisp/progmodes/python.elc" . 86989) (byte-code "\300\301!\302\303E\207" [read-string "Python command: " nil t] 3)] "print(1223)\nprint(42324)")
  python-shell-send-string("print(1223)\nprint(42324)")
  eval((python-shell-send-string "print(1223)\nprint(42324)") nil)
  eval-expression((python-shell-send-string "print(1223)\nprint(42324)") nil)
  funcall-interactively(eval-expression (python-shell-send-string "print(1223)\nprint(42324)") nil)
  call-interactively(eval-expression nil nil)
  command-execute(eval-expression)
1

I finally notice that there are situations that python-shell-send-string and python-shell-send-file call each other.

The function python-shell-send-string use \n to identify whether it should send a string or a file. Adding a condition to avoid this situation should work. (NOT fully tested)

(defun python-send-with-time-record (orig-func string &optional process msg)
  (let* ((prefix (string-trim-left (substring string 0 (min (length string) 30))))
         (wrap (if (string-match "^\\(import codecs, os;__pyfile\\|from IPython.core.completerlib\\|def __PY[DOC\\|THON]\\)" string)
                   string
                 (mapconcat #'identity
                            `("import time"
                              "_EMACS_PYTHON_START_TIME = time.time()"
                              ,(if (string-suffix-p ";" string) (substring string 0 -1) string)
                              "print(' (took: {elapsed:.2e} s)'.format(elapsed=time.time() - _EMACS_PYTHON_START_TIME))")
                            "\n"))))
    (funcall orig-func wrap process msg)))
(advice-add 'python-shell-send-string :around #'python-send-with-time-record)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.