For learning Python, read the basic tutorial. If you already know Python, perhaps check out some best practices. I initially stole my Emacs setup from Gabriel Elanaro’s git project, but now it mostly revolves around the Elpy Project.
Development for Python should be isolated in virtual environments, where Project A doesn’t need to change the dependencies for Project B. First, some background (feel free to skip this section).
First, we used virtualenv, which created a venv
in every project’s
directory. However, after using it a while…
you might end up with a lot of virtual environments littered across your system, and its possible you’ll forget their names or where they were placed.
So, we moved to the virtualenvwrapper project, as it…
provides a set of commands which makes working with virtual environments much more pleasant. It also places all your virtual environments in one place.
Note: To use this, you’d call, mkvirtualenv venv
to create an
environment, and work on a virtual environment, with workon venv
.
In my day job, project often have a different version of Python
(like 2.7.5
for Project B, and 2.6.6 for some legacy Project A).
To solve this, I now use pyenv for all my Pythonic virtualization
needs.
Begin with installing these packages on a Mac:
brew install pyenv pyenv-virtualenv pyenv-virtualwrapper
Or, if on Linux, let’s do it neckbeard-style:
git clone https://github.com/yyuu/pyenv.git ~/.pyenv
git clone https://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv
Next, use pip to install virtualenv globally.
sudo pip install virtualenv
Finally, change the .profile to add these to the basic shell:
export PYENV_ROOT="${HOME}/.pyenv"
if [ -d "${PYENV_ROOT}" ]; then
export PATH="${PYENV_ROOT}/bin:${PATH}"
eval "$(pyenv init -)"
fi
Each project will have its own collection of pip libraries, called an environment. Each environment, however, can have a particular version of Python…
For a given project, we first install a particular Python version
for it using the pyenv
command:
pyenv install 2.6.6
What versions are installed? Run:
pyenv versions
For a new project, create a new environment, and optionally specify the version of Python you want to use:
pyenv global 3.4.2 # Step 1. Set the version
pyenv virtualenv foobar # Step 2. Create environment
pyenv activate foobar # STep 3. Use environment
Now commands like pip
will isolate the module for this particular
environment.
Note: To get all the lovelies found in this configuration file, run the following for each new virtual environment:
pip install --upgrade jedi flake8 pep8 importmagic autopep8 yapf nose
To use an environment you previously created, run:
pyenv activate foobar
And deactivate it with:
pyenv deactivate
Want to set up a default version that isn’t the one that came with your computer:
pyenv global 2.7.5
Or set up a default environment:
pyenv global foobar
Typically, you will have an environment tied to a particular project, and since each project has its own directory, tie a directory to a virtual environment:
pyenv local 2.6.6@foobar
While, the Elpy Project deals with the virtualenvwrapper
(using the
pyvenv-workon
function), we need to install pyenv-mode for Emacs:
(use-package pyenv-mode
:ensure t
:config
(defun projectile-pyenv-mode-set ()
"Set pyenv version matching project name."
(let ((project (projectile-project-name)))
(if (member project (pyenv-mode-versions))
(pyenv-mode-set project)
(pyenv-mode-unset))))
(add-hook 'projectile-switch-project-hook 'projectile-pyenv-mode-set)
(add-hook 'python-mode-hook 'pyenv-mode))
To automatically use the correct virtual environment (based on the
contents of a projects, .python-version
file, use pyenv-mode-auto:
(use-package pyenv-mode-auto
:ensure t)
Note: Since Elpy runs Python in the home directory, it doesn’t deal
with any local environment, so only use pyenv global
.
WSGI files are just Python files in disguise, so tell them to use the Python environment.
Careful with the tabs, my friend.
(use-package python
:mode ("\\.py\\'" . python-mode)
("\\.wsgi$" . python-mode)
:interpreter ("python" . python-mode)
:init
(setq-default indent-tabs-mode nil)
:config
(setq python-indent-offset 4)
(add-hook 'python-mode-hook 'smartparens-mode)
(add-hook 'python-mode-hook 'color-identifiers-mode))
Standardized on the pep8
project, just make sure you’ve install the
Flake8 library.
pip install --upgrade flake8
Flycheck automatically supports Python with Flake8. To use it, set the virtual environment, and the errors should appear automatically.
Unit test and code coverage tool for Python now comes to Emacs with Python Nose.
The ELPY project automatically adds support for testing.
For auto-completion (and refactoring) for Python, you get two choices: Rope or Jedi. Rope, while claiming more features, seems to crash and not work. Besides, Jedi is a better name. See this article, and maybe these instructions.
Elpy will automatically use either, however, I want to hook Jedi to Company mode:
(use-package jedi
:ensure t
:init
(add-to-list 'company-backends 'company-jedi)
:config
(use-package company-jedi
:ensure t
:init
(add-hook 'python-mode-hook (lambda () (add-to-list 'company-backends 'company-jedi)))
(setq company-jedi-python-bin "python")))
While ELPY tries to be the all-in-one Python IDE, Anaconda is
thinner wrapper around Jedi, which seems to work a bit better for me
and the fact that I need to use pyenv
(instead of pyvenv
).
(use-package anaconda-mode
:ensure t
:init (add-hook 'python-mode-hook 'anaconda-mode)
(add-hook 'python-mode-hook 'anaconda-eldoc-mode)
:config (use-package company-anaconda
:ensure t
:init (add-hook 'python-mode-hook 'anaconda-mode)
(eval-after-load "company"
'(add-to-list 'company-backends '(company-anaconda :with company-capf)))))
Oh, make sure we have Company-Anaconda configured too.
According to the ELPY Web Site, grab the latest version:
(add-to-list 'package-archives
'("elpy" . "http://jorgenschaefer.github.io/packages/"))
Then call:
M-x package-initialize
M-x package-install
…elpy
Once this has been installed, we can enable it:
(use-package elpy
:ensure t
:commands elpy-enable
:init (with-eval-after-load 'python (elpy-enable))
:config
(electric-indent-local-mode -1)
(delete 'elpy-module-highlight-indentation elpy-modules)
(delete 'elpy-module-flymake elpy-modules)
(defun ha/elpy-goto-definition ()
(interactive)
(condition-case err
(elpy-goto-definition)
('error (xref-find-definitions (symbol-name (symbol-at-point))))))
:bind (:map elpy-mode-map ([remap elpy-goto-definition] .
ha/elpy-goto-definition)))
Since ELPY is not a simple mode, but a collection of smaller modes
stitched together, we have to call with-eval-after-load
(see this discussion)
As the final bit of customization, first activate a virtual
environment with M-x pyvenv-workon
, and then run: M-x elpy-config
See the documentation for details, but:
C-c C-f
- Find Python file
C-c C-s
- Grep for a Python symbol
C-c C-z
- Switch to the Python Shell
C-c C-c
- Send region to the Python interpreter
Note: The elpy-goto-definition is nice and all if you have a full project with a running interpreter, but I want to use tags as a fallback. However, since the function throws an error, I can’t simply advice the function, like:
(advice-add 'elpy-goto-definition :after-until 'find-tag)
Instead, I had to create a function wrapper.
Make sure that we can simply require
this library.
(provide 'init-python)
Before you can build this on a new system, make sure that you put
the cursor over any of these properties, and hit: C-c C-c