Python
elpy
Apparently, elpy is the way to go when I want to do Python development in emacs. We configure it here.
Elpy automatically activates highlight-identation-mode
, which is quite ugly
in my opinion. It also uses flymake instead of flycheck by default. So let's
only activate the modules we actually want.
(use-package elpy :ensure t :commands (elpy-enable elpy-disable) :init (elpy-enable) ;; autoformat on save (defun do.python/toggle-autoformat () "Toggle yapf autoformat on save." (interactive) (make-variable-buffer-local 'before-save-hook) (if (member #'elpy-yapf-fix-code before-save-hook) (progn (remove-hook 'before-save-hook #'elpy-yapf-fix-code) (message "Disabled yapf autoformat.")) (add-hook 'before-save-hook #'elpy-yapf-fix-code) (message "Enabled yapf autoformat."))) (defun do.python/enable-autoformat () "Enable yapf autoformat on save" (make-variable-buffer-local 'before-save-hook) (add-hook 'before-save-hook #'elpy-yapf-fix-code) (message "Enabled yapf autoformat.")) (add-hook 'python-mode-hook #'do.python/enable-autoformat) (defun do.python/send-region-or-group (&optional arg) "Send the active region or the current group to the Python shell." (interactive "P") (if (use-region-p) (elpy-shell-send-region-or-buffer arg) (elpy-shell-send-group arg))) :bind (:map elpy-mode-map ("C-c C-c" . do.python/send-region-or-group)) :config (setq elpy-rpc-backend "jedi") (setq elpy-modules (quote (elpy-module-company elpy-module-eldoc elpy-module-pyvenv elpy-module-yasnippet elpy-module-sane-defaults))))
Windows
On windows, I use the conda package manager. To make this work with elpy, I need some extra configuration, including the conda.el package.
(when on-windows (defvar do.python.windows/anaconda-home "" "The path to the anaconda home directory. Usually C:\\Users\\username\\anaconda3.") (defvar do.python.windows/emacs-env "emacs" "The name of the default conda env to use for emacs. This cannot be 'base'. To make an env called 'emacs', do: conda create --name emacs python=3.8") ;; we use conda.el to set up PATH and enable the rest of the user-specified "emacs" environment. (use-package conda :ensure t :after elpy :init (setq conda-anaconda-home do.python.windows/anaconda-home) (setq conda-env-home-directory do.python.windows/anaconda-home) (conda-env-initialize-eshell) (conda-env-activate "emacs") (let ((emacs-venv-path (concat (file-name-as-directory do.python.windows/anaconda-home) "\\envs\\" do.python.windows/emacs-env))) ;; make this work with elpy (setq elpy-rpc-virtualenv-path emacs-venv-path) (pyvenv-activate emacs-venv-path))))
Inferior python
The inferior python shell is set in my site file. Here I just set the hooks and enable font locking.
(add-hook 'inferior-python-mode-hook 'no-trailing-whitespace) (add-hook 'inferior-python-mode-hook '(lambda () (setq-local ml-interactive? t))) ;; from https://elpy.readthedocs.io/en/latest/customization_tips.html#enable-full-font-locking-of-inputs-in-the-python-shell (advice-add 'elpy-shell--insert-and-font-lock :around (lambda (f string face &optional no-font-lock) (if (not (eq face 'comint-highlight-input)) (funcall f string face no-font-lock) (funcall f string face t) (python-shell-font-lock-post-command-hook)))) (advice-add 'comint-send-input :around (lambda (f &rest args) (if (eq major-mode 'inferior-python-mode) (cl-letf ((g (symbol-function 'add-text-properties)) ((symbol-function 'add-text-properties) (lambda (start end properties &optional object) (unless (eq (nth 3 properties) 'comint-highlight-input) (funcall g start end properties object))))) (apply f args)) (apply f args))))
Toggle Python versions (Linux only)
The following functions let me toggle between python 2 and 3 environments. I only use this on my linux machine. Also, this is probably a huge hack and will be removed soon.
(unless on-windows (defvar do.python/python-version "3" "The current python version we are using.") (defun do.python/set-python-version (ver) "Set the current python version." (elpy-disable) (setq do.python/python-version ver) (setq python-shell-interpreter (concat "ipython" do.python/python-version)) (setq elpy-rpc-python-command (concat "python" do.python/python-version)) (setenv "WORKON_HOME" (concat (file-name-as-directory (expand-file-name "~/python_venv")) "py" do.python/python-version)) (setenv "VIRTUALENVWRAPPER_PYTHON" (executable-find (concat "python" do.python/python-version))) (setenv "VIRTUALENVWRAPPER_VIRTUALENV" (concat "/usr/bin/virtualenv" do.python/python-version)) (setenv "VIRTUAL_ENV_DISABLE_PROMPT" "TRUE") (setenv "DO_PYTHON_VERSION" do.python/python-version) (elpy-enable)) (defun use-python2 () (interactive) (do.python/set-python-version "2")) (defun use-python3 () (interactive) (do.python/set-python-version "3")) (use-python3))
When I activate a virtualenv, I also want to set the LD_LIBRARY_PATH
variable
(mainly for SWIG)
(unless on-windows (defun do.python/pyvenv-activate-and-set-ld-path (fun directory) "After activating the virtualenv DIRECTORY, set the environment variable LD_LIBRARY_PATH." (let ((ld_path (or (getenv "LD_LIBRARY_PATH") ""))) (apply fun (list directory)) (setq process-environment (append (list (concat "LD_LIBRARY_PATH=" ld_path ":" directory "/lib")) process-environment)))) (add-function :around (symbol-function #'pyvenv-activate) #'do.python/pyvenv-activate-and-set-ld-path))