aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/progmodes/python.el
diff options
context:
space:
mode:
authorLiu Hui2026-01-30 18:10:30 +0800
committerEli Zaretskii2026-02-21 16:51:28 +0200
commitf2b99c4c0b1cca4360a6c516ca99e716e531fae5 (patch)
tree206bb3eb550317a3b597c2cca5ac2f2c80e370bd /lisp/progmodes/python.el
parent6f29a0ca7b179f2fa909bd27de69aa113ceab01e (diff)
downloademacs-f2b99c4c0b1cca4360a6c516ca99e716e531fae5.tar.gz
emacs-f2b99c4c0b1cca4360a6c516ca99e716e531fae5.zip
Use a custom Pdb class in the Python shell
The custom Pdb class enables native completion in pdb by wrapping the pdb's native completer. It also makes necessary function definitions like __PYTHON_EL_* available between pdb frames, and enables non-native completion/ffap/eldoc functionalities when debugging inside python modules. * lisp/progmodes/python.el (python-shell-send-setup-code): Fix the separator between python-shell-setup-codes. (python-shell-completion-native-setup): Move common completion setup code ... (python-shell-completion-setup-code): ... here. (python-shell-completion-at-point): Enable native completion for pdb and respect the delimiter of pdb completer. (python-shell-pdb-setup-code): New variable. (python-shell-comint-watch-for-first-prompt-output-filter): Send setup codes only once. (python-ffap-module-path, python-eldoc--get-doc-at-point): Stop sending setup code in every function call. * test/lisp/progmodes/python-tests.el (python-tests--pdb-1) (python-shell-pdb-1): New test. * etc/NEWS: Mention the change. (bug#80182)
Diffstat (limited to 'lisp/progmodes/python.el')
-rw-r--r--lisp/progmodes/python.el282
1 files changed, 155 insertions, 127 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index c544c845dff..0b4a773516a 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -3736,11 +3736,18 @@ It is used when sending file names to remote Python processes.")
3736 (format "exec(%s)\n" (python-shell--encode-string string)))))) 3736 (format "exec(%s)\n" (python-shell--encode-string string))))))
3737 ;; Bootstrap: the normal definition of `python-shell-send-string' 3737 ;; Bootstrap: the normal definition of `python-shell-send-string'
3738 ;; depends on the Python code sent here. 3738 ;; depends on the Python code sent here.
3739 (python-shell-send-string-no-output python-shell-setup-code)
3740 (python-shell-send-string-no-output python-shell-eval-setup-code) 3739 (python-shell-send-string-no-output python-shell-eval-setup-code)
3741 (python-shell-send-string-no-output python-shell-eval-file-setup-code)) 3740 (python-shell-send-string-no-output python-shell-eval-file-setup-code))
3742 (with-current-buffer (current-buffer) 3741 (with-current-buffer (current-buffer)
3743 (let ((inhibit-quit nil)) 3742 (let ((inhibit-quit nil))
3743 (python-shell-send-string
3744 (mapconcat #'symbol-value '(python-shell-setup-code
3745 python-shell-completion-setup-code
3746 python-shell-pdb-setup-code
3747 python-ffap-setup-code
3748 python-eldoc-setup-code)
3749 "\n"))
3750 (python-shell-accept-process-output (python-shell-get-process))
3744 (python-shell-readline-detect) 3751 (python-shell-readline-detect)
3745 (run-hooks 'python-shell-first-prompt-hook)))))) 3752 (run-hooks 'python-shell-first-prompt-hook))))))
3746 output) 3753 output)
@@ -4432,7 +4439,8 @@ This function takes the list of setup code to send from the
4432 ((symbolp elt) (symbol-value elt)) 4439 ((symbolp elt) (symbol-value elt))
4433 (t ""))) 4440 (t "")))
4434 python-shell-setup-codes 4441 python-shell-setup-codes
4435 "\n\nprint ('python.el: sent setup code')")))) 4442 "\n")
4443 "\nprint ('python.el: sent setup code')")))
4436 (python-shell-send-string code process) 4444 (python-shell-send-string code process)
4437 (python-shell-accept-process-output process)))) 4445 (python-shell-accept-process-output process))))
4438 4446
@@ -4506,16 +4514,82 @@ def __PYTHON_EL_get_completions(text):
4506 finally: 4514 finally:
4507 if getattr(completer, 'PYTHON_EL_WRAPPED', False): 4515 if getattr(completer, 'PYTHON_EL_WRAPPED', False):
4508 completer.print_mode = True 4516 completer.print_mode = True
4509 return json.dumps(completions)" 4517 return json.dumps(completions)
4510 "Code used to setup completion in inferior Python processes." 4518
4511 :type 'string) 4519def __PYTHON_EL_wrap_completer():
4520 import readline
4521 completer = readline.get_completer()
4512 4522
4513(defun python-shell-completion-send-setup-code () 4523 if not completer:
4514 "Send `python-shell-completion-setup-code' to inferior Python process." 4524 # Used as last resort to avoid breaking customizations.
4515 (python-shell-send-string-no-output python-shell-completion-setup-code)) 4525 import rlcompleter
4526 completer = readline.get_completer()
4516 4527
4517(add-hook 'python-shell-first-prompt-hook 4528 if completer and not getattr(completer, 'PYTHON_EL_WRAPPED', False):
4518 #'python-shell-completion-send-setup-code) 4529 class __PYTHON_EL_Completer:
4530 '''Completer wrapper that prints candidates to stdout.
4531
4532 It wraps an existing completer function and changes its behavior so
4533 that the user input is unchanged and real candidates are printed to
4534 stdout.
4535
4536 Returned candidates are '0__dummy_completion__' and
4537 '1__dummy_completion__' in that order ('0__dummy_completion__' is
4538 returned repeatedly until all possible candidates are consumed).
4539
4540 The real candidates are printed to stdout so that they can be
4541 easily retrieved through comint output redirect trickery.
4542 '''
4543
4544 PYTHON_EL_WRAPPED = True
4545
4546 def __init__(self, completer):
4547 self.completer = completer
4548 self.last_completion = None
4549 self.print_mode = True
4550
4551 def __call__(self, text, state):
4552 if state == 0:
4553 # Set the first dummy completion.
4554 self.last_completion = None
4555 completion = '0__dummy_completion__'
4556 else:
4557 completion = self.completer(text, state - 1)
4558
4559 if not completion:
4560 if self.last_completion != '1__dummy_completion__':
4561 # When no more completions are available, returning a
4562 # dummy with non-sharing prefix allow ensuring output
4563 # while preventing changes to current input.
4564 # Coincidentally it's also the end of output.
4565 completion = '1__dummy_completion__'
4566 elif completion.endswith('('):
4567 # Remove parens on callables as it breaks completion on
4568 # arguments (e.g. str(Ari<tab>)).
4569 completion = completion[:-1]
4570 self.last_completion = completion
4571
4572 if completion in (
4573 '0__dummy_completion__', '1__dummy_completion__'):
4574 return completion
4575 elif completion:
4576 # For every non-dummy completion, return a repeated dummy
4577 # one and print the real candidate so it can be retrieved
4578 # by comint output filters.
4579 if self.print_mode:
4580 print (completion)
4581 return '0__dummy_completion__'
4582 else:
4583 return completion
4584 else:
4585 return completion
4586
4587 # Wrap the existing completer function only once.
4588 new_completer = __PYTHON_EL_Completer(completer)
4589 readline.set_completer(new_completer)"
4590 "Code used to setup completion in inferior Python processes."
4591 :type 'string
4592 :version "31.1")
4519 4593
4520(define-obsolete-variable-alias 4594(define-obsolete-variable-alias
4521 'python-shell-completion-module-string-code 4595 'python-shell-completion-module-string-code
@@ -4601,103 +4675,16 @@ except:
4601def __PYTHON_EL_native_completion_setup(): 4675def __PYTHON_EL_native_completion_setup():
4602 try: 4676 try:
4603 import readline 4677 import readline
4678 __PYTHON_EL_wrap_completer()
4604 4679
4680 # Ensure that rlcompleter.__main__ and __main__ are identical.
4681 # (Bug#76205)
4682 import sys
4605 try: 4683 try:
4606 import __builtin__ 4684 __IPYTHON__
4607 except ImportError: 4685 sys.modules['rlcompleter'].__main__ = sys.modules['__main__']
4608 # Python 3 4686 except (NameError, KeyError):
4609 import builtins as __builtin__ 4687 pass
4610
4611 builtins = dir(__builtin__)
4612 is_ipython = ('__IPYTHON__' in builtins or
4613 '__IPYTHON__active' in builtins)
4614
4615 class __PYTHON_EL_Completer:
4616 '''Completer wrapper that prints candidates to stdout.
4617
4618 It wraps an existing completer function and changes its behavior so
4619 that the user input is unchanged and real candidates are printed to
4620 stdout.
4621
4622 Returned candidates are '0__dummy_completion__' and
4623 '1__dummy_completion__' in that order ('0__dummy_completion__' is
4624 returned repeatedly until all possible candidates are consumed).
4625
4626 The real candidates are printed to stdout so that they can be
4627 easily retrieved through comint output redirect trickery.
4628 '''
4629
4630 PYTHON_EL_WRAPPED = True
4631
4632 def __init__(self, completer):
4633 self.completer = completer
4634 self.last_completion = None
4635 self.print_mode = True
4636
4637 def __call__(self, text, state):
4638 if state == 0:
4639 # Set the first dummy completion.
4640 self.last_completion = None
4641 completion = '0__dummy_completion__'
4642 else:
4643 completion = self.completer(text, state - 1)
4644
4645 if not completion:
4646 if self.last_completion != '1__dummy_completion__':
4647 # When no more completions are available, returning a
4648 # dummy with non-sharing prefix allow ensuring output
4649 # while preventing changes to current input.
4650 # Coincidentally it's also the end of output.
4651 completion = '1__dummy_completion__'
4652 elif completion.endswith('('):
4653 # Remove parens on callables as it breaks completion on
4654 # arguments (e.g. str(Ari<tab>)).
4655 completion = completion[:-1]
4656 self.last_completion = completion
4657
4658 if completion in (
4659 '0__dummy_completion__', '1__dummy_completion__'):
4660 return completion
4661 elif completion:
4662 # For every non-dummy completion, return a repeated dummy
4663 # one and print the real candidate so it can be retrieved
4664 # by comint output filters.
4665 if self.print_mode:
4666 print (completion)
4667 return '0__dummy_completion__'
4668 else:
4669 return completion
4670 else:
4671 return completion
4672
4673 completer = readline.get_completer()
4674
4675 if not completer:
4676 # Used as last resort to avoid breaking customizations.
4677 import rlcompleter
4678 completer = readline.get_completer()
4679
4680 if completer and not getattr(completer, 'PYTHON_EL_WRAPPED', False):
4681 # Wrap the existing completer function only once.
4682 new_completer = __PYTHON_EL_Completer(completer)
4683 if not is_ipython:
4684 readline.set_completer(new_completer)
4685 else:
4686 # Ensure that rlcompleter.__main__ and __main__ are identical.
4687 # (Bug#76205)
4688 import sys
4689 try:
4690 sys.modules['rlcompleter'].__main__ = sys.modules['__main__']
4691 except KeyError:
4692 pass
4693 # Try both initializations to cope with all IPython versions.
4694 # This works fine for IPython 3.x but not for earlier:
4695 readline.set_completer(new_completer)
4696 # IPython<3 hacks readline such that `readline.set_completer`
4697 # won't work. This workaround injects the new completer
4698 # function into the existing instance directly:
4699 instance = getattr(completer, 'im_self', completer.__self__)
4700 instance.rlcomplete = new_completer
4701 4688
4702 if readline.__doc__ and 'libedit' in readline.__doc__: 4689 if readline.__doc__ and 'libedit' in readline.__doc__:
4703 raise Exception('''libedit based readline is known not to work, 4690 raise Exception('''libedit based readline is known not to work,
@@ -4921,8 +4908,17 @@ using that one instead of current buffer's process."
4921 ;; Working on a shell buffer: use prompt end. 4908 ;; Working on a shell buffer: use prompt end.
4922 (cdr (python-util-comint-last-prompt)) 4909 (cdr (python-util-comint-last-prompt))
4923 (line-beginning-position))) 4910 (line-beginning-position)))
4911 (prompt-boundaries
4912 (with-current-buffer (process-buffer process)
4913 (python-util-comint-last-prompt)))
4914 (prompt
4915 (with-current-buffer (process-buffer process)
4916 (when prompt-boundaries
4917 (buffer-substring-no-properties
4918 (car prompt-boundaries) (cdr prompt-boundaries)))))
4924 (no-delims 4919 (no-delims
4925 (and (not (if is-shell-buffer 4920 (and (not (string-match-p python-shell-prompt-pdb-regexp prompt))
4921 (not (if is-shell-buffer
4926 (eq 'font-lock-comment-face 4922 (eq 'font-lock-comment-face
4927 (get-text-property (1- (point)) 'face)) 4923 (get-text-property (1- (point)) 'face))
4928 (python-syntax-context 'comment))) 4924 (python-syntax-context 'comment)))
@@ -4946,14 +4942,6 @@ using that one instead of current buffer's process."
4946 (forward-char (length (match-string-no-properties 0))) 4942 (forward-char (length (match-string-no-properties 0)))
4947 (point))))) 4943 (point)))))
4948 (end (point)) 4944 (end (point))
4949 (prompt-boundaries
4950 (with-current-buffer (process-buffer process)
4951 (python-util-comint-last-prompt)))
4952 (prompt
4953 (with-current-buffer (process-buffer process)
4954 (when prompt-boundaries
4955 (buffer-substring-no-properties
4956 (car prompt-boundaries) (cdr prompt-boundaries)))))
4957 (completion-fn 4945 (completion-fn
4958 (with-current-buffer (process-buffer process) 4946 (with-current-buffer (process-buffer process)
4959 (cond ((or (null prompt) 4947 (cond ((or (null prompt)
@@ -4963,13 +4951,7 @@ using that one instead of current buffer's process."
4963 (string-match-p 4951 (string-match-p
4964 python-shell-prompt-pdb-regexp prompt))) 4952 python-shell-prompt-pdb-regexp prompt)))
4965 #'ignore) 4953 #'ignore)
4966 ((or (not python-shell-completion-native-enable) 4954 ((not python-shell-completion-native-enable)
4967 ;; Even if native completion is enabled, for
4968 ;; pdb interaction always use the fallback
4969 ;; mechanism since the completer is changed.
4970 ;; Also, since pdb interaction is single-line
4971 ;; based, this is enough.
4972 (string-match-p python-shell-prompt-pdb-regexp prompt))
4973 (if (or (equal python-shell--block-prompt prompt) 4955 (if (or (equal python-shell--block-prompt prompt)
4974 (string-match-p 4956 (string-match-p
4975 python-shell-prompt-block-regexp prompt)) 4957 python-shell-prompt-block-regexp prompt))
@@ -5051,6 +5033,55 @@ If not try to complete."
5051 5033
5052;;; PDB Track integration 5034;;; PDB Track integration
5053 5035
5036(defconst python-shell-pdb-setup-code
5037 "\
5038def __PYTHON_EL_Pdb_setup():
5039 import pdb
5040
5041 class _PYTHON_EL_Pdb(pdb.Pdb, object):
5042 def __init__(self, *args, **kw):
5043 super(_PYTHON_EL_Pdb, self).__init__(*args, **kw)
5044 import re
5045 self._python_el_def_pattern = re.compile('__(PYTHON_EL|FFAP|PYDOC)_')
5046 self._python_el_defs = {}
5047 for k, v in globals().items():
5048 if self._python_el_def_pattern.match(k):
5049 self._python_el_defs[k] = v
5050
5051 def _python_el_setup(self):
5052 if not hasattr(self, 'curframe') or self.curframe is None:
5053 return
5054 frame_globals = self.curframe.f_globals
5055 if '__PYTHON_EL_eval' not in frame_globals:
5056 for k, v in self._python_el_defs.items():
5057 frame_globals[k] = v
5058 try:
5059 frame_globals['__PYTHON_EL_wrap_completer']()
5060 except Exception as e:
5061 print('failed to setup completer: {}'.format(str(e)))
5062
5063 def preloop(self):
5064 super(_PYTHON_EL_Pdb, self).preloop()
5065 # Trigger precmd/postcmd when entering pdb.
5066 self.cmdqueue.append('pass # __PYTHON_EL_')
5067
5068 def precmd(self, line):
5069 if self._python_el_def_pattern.search(line):
5070 self._real_lastcmd = self.lastcmd
5071 return super(_PYTHON_EL_Pdb, self).precmd(line)
5072
5073 def postcmd(self, stop, line):
5074 self._python_el_setup()
5075 if self._python_el_def_pattern.search(line):
5076 self.lastcmd = self._real_lastcmd
5077 return super(_PYTHON_EL_Pdb, self).postcmd(stop, line)
5078
5079 pdb.Pdb = _PYTHON_EL_Pdb
5080
5081__PYTHON_EL_Pdb_setup()
5082del __PYTHON_EL_Pdb_setup"
5083 "Code used to setup the debugger in inferior Python processes.")
5084
5054(defcustom python-pdbtrack-activate t 5085(defcustom python-pdbtrack-activate t
5055 "Non-nil makes Python shell enable pdbtracking. 5086 "Non-nil makes Python shell enable pdbtracking.
5056Pdbtracking would open the file for current stack frame found in pdb output by 5087Pdbtracking would open the file for current stack frame found in pdb output by
@@ -5691,8 +5722,7 @@ def __FFAP_get_module_path(objstr):
5691 (python-util-comint-end-of-output-p))) 5722 (python-util-comint-end-of-output-p)))
5692 (module-file 5723 (module-file
5693 (python-shell-send-string-no-output 5724 (python-shell-send-string-no-output
5694 (format "%s\nprint(__FFAP_get_module_path(%s))" 5725 (format "print(__FFAP_get_module_path(%s))"
5695 python-ffap-setup-code
5696 (python-shell--encode-string module))))) 5726 (python-shell--encode-string module)))))
5697 (unless (string-empty-p module-file) 5727 (unless (string-empty-p module-file)
5698 (python-util-strip-string module-file)))) 5728 (python-util-strip-string module-file))))
@@ -5815,10 +5845,8 @@ returns will be used. If not FORCE-PROCESS is passed what
5815 ;; enabled. Bug#18794. 5845 ;; enabled. Bug#18794.
5816 (python-util-strip-string 5846 (python-util-strip-string
5817 (python-shell-send-string-no-output 5847 (python-shell-send-string-no-output
5818 (format 5848 (format "print(__PYDOC_get_help(%s))"
5819 "%s\nprint(__PYDOC_get_help(%s))" 5849 (python-shell--encode-string input))
5820 python-eldoc-setup-code
5821 (python-shell--encode-string input))
5822 process))))) 5850 process)))))
5823 (unless (string-empty-p docstring) 5851 (unless (string-empty-p docstring)
5824 docstring))))) 5852 docstring)))))