Editing Python with Emacs

Posted: | Updated:

UPDATE 2: I've since done yet another post on the topic of Emacs and Python editing. Be sure to check that out, as well as my init.el itself.

UPDATE: Please be sure to check out the Part 2 to this entry Where I describe other awesome bits like Jedi! It's an update as well as an errata for this one.

The recent business with JetBrains' licensing is a great example of how quickly things can change (though this seems to have been resolved). Other things, like GNU Emacs for example, don't change too terribly much over very long periods of time.

I really love PyCharm and have been a license holder for several years. It's sort of hard to imagine working on Django projects without some of the neato bells and whistles it provides, and although I'm not 100% certain I won't continue to be a JetBrains customer, this news had made me really think about how far I am from just using Emacs for all of my Python dev work.

Can I do this? Or will I go insane from the lack of convenience?!

Ranting

A wise but stubborn man once told me: "Emacs is a terrible text editor!" ...

First thing's first, I already prefer Emacs to PyCharm for non-Django Python work. But working with Django (and web projects in general) is a whole different beast; you're almost guaranteed to be working with HTML, CSS, Javascript, and maybe even SQL (or at the very least interact with a database via an ORM.)

I don't use PyCharm to manage my database, I don't use its hooks into manage.py, and I don't really use any of the build system/debugger features either. I prefer to do these things via the command line, with scripts or a makefile-based system.

Ultimately, what really hits the sweet spot for me about PyCharm is the awesome syntax-checking and code completion. That's for any Python, HTML, CSS, or Javascript. By and large, it is seemingly flawless and very quick. Especially in HTML templates, having the editor know about, and complete {% if %} and the like is super handy.

Can Emacs do this?

Indeed that is the question. More precisely: can I have awesome syntax-checking and code completion in Emacs (and will it be at least as good as what I have with PyCharm?)

The answer to those questions, respectively are: yes absolutely and yes mostly, but it takes a bit of work to get there. Elsewhere on this site I show off my init.el, and while it isn't necessary to cite the entire file everything I'm about to discuss is in there.

In the end, I want something that is:

You can get these things for Emacs, to be sure, but for now I really just want fast and accurate editing.

Packages and MELPA

We can take care of the first point with a couple of lines of Emacs lisp:

(defvar package-list
      '(clean-aindent-mode company diff-hl dtrt-indent
        elpy flycheck function-args web-mode))

(require 'package)
(add-to-list 'package-archives
             '("melpa" . "http://melpa.milkbox.net/packages/") t)
(package-initialize)

(or (file-exists-p package-user-dir)
    (package-refresh-contents))

(dolist (package package-list)
  (unless (package-installed-p package)
    (package-install package)))

The lists the packages we want to install and then iterates over them, ensuring they are installed. The package list includes everything you'll need for the functionality we want, with some notable highlights:

I strongly recommend checking out the Elpy quick install guide before proceeding, there are a few pip packages you'll need which is covered there.

Arguably, web-mode and the indent packages are optional, but I find them to be more helpful than intrusive. YMMV.

Setting up

Now that we've got all these packages, we need to actually use them. Add this to your init.el, below what you've already got:

(require 'elpy)
(elpy-enable)

(require 'flycheck)
(add-hook 'after-init-hook #'global-flycheck-mode)

(require 'function-args)
(fa-config-default)

(require 'web-mode)
(add-to-list 'auto-mode-alist '("\\.tpl\\'" . web-mode))

To django-mode, or not to django-mode?

At this time, I'm not totally sure about django-mode. The project appears to need a maintainer, and for sure is a bit off on current Django best practices. For now python-mode and web-mode seem to fill in the gaps since I don't really care about the project management bits and the manage.py helpers.

Custom bits

The final icing on the cake are a few functions which will allow you to easily switch between Python versions within Emacs, as well as one that makes multi-line commenting much easier:

(defun use-python343 ()
  "Point to Python 3.4.3 for `elpy-mode', `flycheck-mode', and `python-mode'."
  (interactive)
  (setq
   elpy-rpc-python-command "/opt/python343/bin/python3.4m"
   elpy-rpc-pythonpath "/opt/python343/lib/python3.4/site-packages"
   flycheck-python-flake8-executable "/opt/python343/bin/flake8"
   python-check-command "/opt/python343/bin/pyflakes"
   python-shell-interpreter "/opt/python343/bin/ipython3"))

(defun use-pyenv-python3 ()
  "Point to Python 3 for `elpy-mode', `flycheck-mode', and `python-mode'."
  (interactive)
  (setq
   elpy-rpc-python-command "~/.pyenv/versions/3.4.3/bin/python3.4m"
   elpy-rpc-pythonpath "~/.pyenv/versions/3.4.3/lib/python3.4/site-packages"
   flycheck-python-flake8-executable "~/.pyenv/versions/3.4.3/bin/flake8"
   python-check-command "~/.pyenv/versions/3.4.3/bin/pyflakes"
   python-shell-interpreter "~/.pyenv/versions/3.4.3/bin/ipython3"))

(defun use-pyenv-python2 ()
  "Point to Python 2 for `elpy-mode', `flycheck-mode', and `python-mode'."
  (interactive)
  (setq
   elpy-rpc-python-command "~/.pyenv/versions/2.7.10/bin/python2.7"
   elpy-rpc-pythonpath "~/.pyenv/versions/2.7.10/lib/python2.7/site-packages"
   flycheck-python-flake8-executable "~/.pyenv/versions/2.7.10/bin/flake8"
   python-check-command "~/.pyenv/versions/2.7.10/bin/pyflakes"
   python-shell-interpreter "~/.pyenv/versions/2.7.10/bin/ipython"))

(defun use-system-python3 ()
  "Use the system python3 for `elpy-mode', `flycheck-mode', and `python-mode'."
  (interactive)
  (setq
   elpy-rpc-python-command "/usr/bin/python3.4m"
   elpy-rpc-pythonpath "/usr/local/lib/python3.4/dist-packages"
   flycheck-python-flake8-executable "/usr/bin/flake8"
   python-check-command "/usr/bin/pyflakes3"
   python-shell-interpreter "/usr/bin/ipython3"))

(defun use-system-python2 ()
  "Use the system python2 for `elpy-mode', `flycheck-mode', and `python-mode'."
  (interactive)
  (setq
   elpy-rpc-python-command "/usr/bin/python2.7"
   elpy-rpc-pythonpath "/usr/local/lib/python2.7/dist-packages"
   flycheck-python-flake8-executable "/usr/bin/flake8"
   python-check-command "/usr/bin/pyflakes"
   python-shell-interpreter "/usr/bin/ipython"))

(defun toggle-comment ()
  "Toggle comments on the current line or highlighted region."
  (interactive)
  (if mark-active
      (let ((mark (mark))
            (point (point)))
        (if (> (mark) (point))
            (comment-or-uncomment-region
             point
             mark)
          (comment-or-uncomment-region
           mark
           point)))
    (comment-or-uncomment-region
     (line-beginning-position)
     (line-end-position))))

(use-python343)

With this, we can simply fire off a M-x use-python-X to change context to a different python version (and default to a custom built Python 3.4.3).

There is, of course, much more you can do (and much more that I do with my own config), but for now we're just talking about making a nice Python editor; you may tweak as needed after all this.

Conclusion

Does Emacs suffice as an intelligent, helpful development tool? Yes it totally does, and in many ways I like it better than PyCharm. Although PyCharm seems to be more tightly integrated in many ways (in particular with regard to Django) Emacs seems to fill in pretty much all of those gaps - and in some ways I like Emac's completion and error checking better! Your mileage may vary, of course, but hopefully this can serve as an example of how to get a nice, intelligent Python development editor using FOSS tools.

Feel free to take a gander at my full config, and happy travels on your Emacs rabbit hole journey!

In action!

This page was last modified on: 2020-07-26