From 24ae05251587fbba4687544ec57565c8bc48071a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Fabi=C3=A1n=20Ezequiel=20Gallina?= Date: Sat, 22 Aug 2015 20:42:04 -0300 Subject: [PATCH] python.el: fallback completion, ffap and eldoc setup enhancements Setup codes are now sent continuously so that the current frame is always taken into account. This allows working within debuggers and always keeping a fresh version of setup codes that will return proper results. * lisp/progmodes/python.el (python-shell-setup-codes): Cleanup. (python-shell-send-setup-code): Send code only when python-shell-setup-codes is non-nil. (python-shell-completion-string-code): Cleanup trailing newline. (python-shell-completion-get-completions): Always use python-shell-completion-setup-code. (python-ffap-setup-code): Work with any object, not only modules. (python-ffap-string-code): Cleanup trailing newline. (python-ffap-module-path): Always use python-ffap-setup-code. (python-eldoc-string-code): Cleanup trailing newline. (python-eldoc--get-doc-at-point): Always use python-eldoc-setup-code. Return non-nil only if docstring is found. --- lisp/progmodes/python.el | 125 +++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 58 deletions(-) diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index f65380be34f..3bfc5cf4b6f 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -2008,9 +2008,7 @@ virtualenv." (define-obsolete-variable-alias 'python-shell-virtualenv-path 'python-shell-virtualenv-root "25.1") -(defcustom python-shell-setup-codes '(python-shell-completion-setup-code - python-ffap-setup-code - python-eldoc-setup-code) +(defcustom python-shell-setup-codes nil "List of code run by `python-shell-send-setup-codes'." :type '(repeat symbol) :group 'python) @@ -3118,18 +3116,18 @@ t when called interactively." "Send all setup code for shell. This function takes the list of setup code to send from the `python-shell-setup-codes' list." - (let ((process (python-shell-get-process)) - (code (concat - (mapconcat - (lambda (elt) - (cond ((stringp elt) elt) - ((symbolp elt) (symbol-value elt)) - (t ""))) - python-shell-setup-codes - "\n\n") - "\n\nprint ('python.el: sent setup code')"))) - (python-shell-send-string code process) - (python-shell-accept-process-output process))) + (when python-shell-setup-codes + (let ((process (python-shell-get-process)) + (code (concat + (mapconcat + (lambda (elt) + (cond ((stringp elt) elt) + ((symbolp elt) (symbol-value elt)) + (t ""))) + python-shell-setup-codes + "\n\nprint ('python.el: sent setup code')")))) + (python-shell-send-string code process) + (python-shell-accept-process-output process)))) (add-hook 'inferior-python-mode-hook #'python-shell-send-setup-code) @@ -3187,7 +3185,7 @@ else: :group 'python) (defcustom python-shell-completion-string-code - "';'.join(__PYTHON_EL_get_completions('''%s'''))\n" + "';'.join(__PYTHON_EL_get_completions('''%s'''))" "Python code used to get a string of completions separated by semicolons. The string passed to the function is the current python name or the full statement in the case of imports." @@ -3490,28 +3488,22 @@ completion." ;; Check whether a prompt matches a pdb string, an import ;; statement or just the standard prompt and use the ;; correct python-shell-completion-*-code string - (cond ((and (string-match - (concat "^" python-shell-prompt-pdb-regexp) prompt)) - ;; Since there are no guarantees the user will remain - ;; in the same context where completion code was sent - ;; (e.g. user steps into a function), safeguard - ;; resending completion setup continuously. - (concat python-shell-completion-setup-code - "\nprint (" python-shell-completion-string-code ")")) - ((string-match - python-shell--prompt-calculated-input-regexp prompt) - python-shell-completion-string-code) - (t nil))) + (when (string-match python-shell--prompt-calculated-input-regexp prompt) + ;; Since there are no guarantees the user will remain + ;; in the same context where completion code was sent + ;; (e.g. user steps into a function), safeguard + ;; resending completion setup continuously. + (concat python-shell-completion-setup-code + "\nprint (" python-shell-completion-string-code ")"))) (subject (or import input))) - (and completion-code - (> (length input) 0) - (let ((completions - (python-util-strip-string - (python-shell-send-string-no-output - (format completion-code subject) process)))) - (and (> (length completions) 2) - (split-string completions - "^'\\|^\"\\|;\\|'$\\|\"$" t))))))) + (when (and completion-code (> (length input) 0)) + (let ((completions + (python-util-strip-string + (python-shell-send-string-no-output + (format completion-code subject) process)))) + (when (> (length completions) 2) + (split-string completions + "^'\\|^\"\\|;\\|'$\\|\"$" t))))))) (defun python-shell-completion-at-point (&optional process) "Function for `completion-at-point-functions' in `inferior-python-mode'. @@ -4055,13 +4047,22 @@ The skeleton will be bound to python-skeleton-NAME." ;;; FFAP (defcustom python-ffap-setup-code - "def __FFAP_get_module_path(module): + " +def __FFAP_get_module_path(objstr): try: - import os - path = __import__(module).__file__ - if path[-4:] == '.pyc' and os.path.exists(path[0:-1]): - path = path[:-1] - return path + import inspect + import os.path + # NameError exceptions are delayed until this point. + obj = eval(objstr) + module = inspect.getmodule(obj) + filename = module.__file__ + ext = os.path.splitext(filename)[1] + if ext in ('.pyc', '.pyo'): + # Point to the source file. + filename = filename[:-1] + if os.path.exists(filename): + return filename + return '' except: return ''" "Python code to get a module path." @@ -4069,7 +4070,7 @@ The skeleton will be bound to python-skeleton-NAME." :group 'python) (defcustom python-ffap-string-code - "__FFAP_get_module_path('''%s''')\n" + "__FFAP_get_module_path('''%s''')" "Python code used to get a string with the path of a module." :type 'string :group 'python) @@ -4084,9 +4085,12 @@ The skeleton will be bound to python-skeleton-NAME." nil (let ((module-file (python-shell-send-string-no-output - (format python-ffap-string-code module) process))) - (when module-file - (substring-no-properties module-file 1 -1)))))) + (concat + python-ffap-setup-code + "\nprint (" (format python-ffap-string-code module) ")") + process))) + (unless (zerop (length module-file)) + (python-util-strip-string module-file)))))) (defvar ffap-alist) @@ -4172,13 +4176,13 @@ See `python-check-command' for the default." doc = doc.splitlines()[0] except: doc = '' - print (doc)" + return doc" "Python code to setup documentation retrieval." :type 'string :group 'python) (defcustom python-eldoc-string-code - "__PYDOC_get_help('''%s''')\n" + "__PYDOC_get_help('''%s''')" "Python code used to get a string with the documentation of an object." :type 'string :group 'python) @@ -4204,15 +4208,20 @@ returns will be used. If not FORCE-PROCESS is passed what `python-shell-get-process' returns is used." (let ((process (or force-process (python-shell-get-process)))) (when process - (let ((input (or force-input - (python-eldoc--get-symbol-at-point)))) - (and input - ;; Prevent resizing the echo area when iPython is - ;; enabled. Bug#18794. - (python-util-strip-string - (python-shell-send-string-no-output - (format python-eldoc-string-code input) - process))))))) + (let* ((input (or force-input + (python-eldoc--get-symbol-at-point))) + (docstring + (when input + ;; Prevent resizing the echo area when iPython is + ;; enabled. Bug#18794. + (python-util-strip-string + (python-shell-send-string-no-output + (concat + python-eldoc-setup-code + "\nprint(" (format python-eldoc-string-code input) ")") + process))))) + (unless (zerop (length docstring)) + docstring))))) (defun python-eldoc-function () "`eldoc-documentation-function' for Python. -- 2.39.5