Emacs As A Python IDE - Debugging

Since the advent of language server protocols (LSP) and the default inclusion of native JSON parsing in Emacs, I've read online several blog articles and watched videos from System Crafters on how things have improved for the text editor (although it can be used as a OS). Moreover, the experimental project mode gives a built-in project management that seems to have fewer features than projectile, but provides a ready-to-use tool which requires minimal configuration, if none at all, to be already useful.

I love Emacs, however, when dealing with larger code bases, it can be difficult to navigate through a project or debugging. This is why I have always ended up using Emacs for personal projects, notes, reports in org-mode, ticket management with org-jira or scripting; while for larger projects I have always resorted to intellij IDE's like PyCharm.

I cannot deny I felt ashamed, dreaming of a simpler way of developing and debugging python code that did not involve multiple packages, hundreds of lines of configuration to then have a debugging experience that still was not even close to the one which PyCharm provides.

Then, when I saw the things that could be achieved with lsp-mode, lsp-ui and dap-mode with so little configuration I had to pull my jaw up more than once. In the end, I figured, I only needed few things to have a basic good development & debugging experience in Python using Emacs:

  • lsp-mode & lsp-ui for the language server
  • lsp-treemacs to have tree-like visualization of references and symbols
  • dap-mode for debugging using debugpy
  • with-venv is needed if you want to automatically activate virtual envs based on pwd.
  • pyvenv-mode to manage VE's

The result is a very nice debugging experience where you have windows to look at the state of execution and inspect the objects and can easily evaluate expressions or set conditional breakpoints.

Moreover, with dap you can set up templates, which are like PyCharm configurations, where you can specify which module to run from which directory, set environment variables and more. The only thing you may want to add is this from https://github.com/emacs-lsp/dap-mode/issues/202:

  (defun dap-python--pyenv-executable-find (command)
    (with-venv (executable-find "python")))

What I have included are a couple of keybindings for stepping into functions and stepping over, I have also included the fix to user virtual environments easily.

(use-package dap-mode
  :after lsp-mode
  :commands dap-debug
  :bind (("C-c <C-right>" . dap-next)
	 ("C-c <C-down>" . dap-step-in)
	 )
  
  :hook ((python-mode . dap-ui-mode)
	 (python-mode . dap-mode))
  :config
  
  (require 'dap-python)

  ;; Temporal fix
  (defun dap-python--pyenv-executable-find (command)
    (with-venv (executable-find "python3")))
  (dap-register-debug-template
  "Python :: MyProject Main"
  (list :type "python"
        :args ""
        :cwd "~/myproject/"
	:justMyCode :json-false
        :module nil
	:env '(("DEV" . "1"))
	;:target-module (expand-file-name "~/src/myapp/.env/bin/myapp")
        :program "~/myproject/main.py"
        :request "launch"
        :name "Python :: MyProject Main")))

Finally, I have included some key-bindings to perform common actions:

  :bind (("C-c <C-right>" . dap-next)
	 ("C-c <C-down>" . dap-step-in)
	 ("C-c <C-up>" . dap-step-out)
	 ("C-c <M-right>" . dap-continue)
	 ("C-c b" . dap-breakpoint-toggle))

After using these packages for some months for some personal projects, I can say that the python development and debugging experience within Emacs are much better than what they used to be some years ago. Also, if you are used to more mouse interactions (like I am in PyCharm), you will find that some of those are already available in lsp-mode, like clicking on a function to go to its definition or clicking on a inherited class. If you include other well known packages such as magit, helm, yasnippets, etc… You can do most of your work without ever leaving Emacs.

/img/emacs-python-dap.png