Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Turn pyenv-mode into a local mode and introduce global-pyenv-mode #30

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 37 additions & 25 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ Integrate Fabián E. Gallina `python.el`_ with pyenv_ tool. This allow
packages which already use python.el (like python-django_) got pyenv
virtual environments support out-of-the-box.

Default automatic integration with `projectile`_ is available since 0.2.0.

Pyenv mode does...
~~~~~~~~~~~~~~~~~~

* Setup ``PYENV_VERSION`` environment variable and
``python-shell-virtualenv-path`` custom variable based on user input
``python-shell-virtualenv-path`` custom variable based on user input or
switching to a projectile project.

Pyenv mode doesn't...
~~~~~~~~~~~~~~~~~~~~~
Expand All @@ -36,12 +39,30 @@ You can simply install package from Melpa_::
Usage
-----

Add following block to your emacs configuration

.. code:: lisp

(pyenv-mode)

This is enough to turn on ``pyenv-mode`` 's for all ``python-mode`` buffers. If
you are using Projectile, switching to a Python project will setup its Pyenv
Python version for you using either the project's ``.python-version`` file or
the project's name.

If you are not using Projectile, you will have to activate the appropriate
Python version like the following while visiting a ``python`` mode buffer::

M-x pyenv-mode-set

Advanced Usage
--------------

If you don't want to use the global ``pyenv-mode`` for whatever reason, you can
still activate ``pyenv-mode``, but you have to use the local mode instead::

.. code:: lisp

(add-hook 'python-mode-hook 'pyenv-local-mode)

Now you are available to specify pyenv python version::

M-x pyenv-mode-set
Expand All @@ -55,6 +76,17 @@ unset current version with::

M-x pyenv-mode-unset

FAQ
---

**Q:** My project has multiple ``.python-version`` files in different
subdirectories, when I switch to a different subproject, ``pyenv-mode`` didn't
switch to a different Python version for me, how come?
**A:** By default Projectile only recognizes a couple types of projects by looking
at specific version control system directories or common configuration files.
You can either put an empty ``.projectile`` file in the directory or `register a
new project type`_.

Goodies
-------

Expand All @@ -65,31 +97,11 @@ happens automatically
* flycheck_ perform syntax checking according to python version you use
* anaconda-mode_ search completions, definitions and references in chosen environment

Projectile integration
``````````````````````

You can switch python version together with current project. Drop
following lines into emacs init file. When use projectile switch
project with ``C-c p p`` key binding ``pyenv-mode`` will activate
environment matched project name.

.. code:: lisp

(require 'pyenv-mode)

(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-after-switch-project-hook 'projectile-pyenv-mode-set)

.. _python.el: http://repo.or.cz/w/emacs.git/blob_plain/master:/lisp/progmodes/python.el
. _python.el: http://repo.or.cz/w/emacs.git/blob_plain/master:/lisp/progmodes/python.el
.. _pyenv: https://github.com/yyuu/pyenv
.. _python-django: https://github.com/fgallina/python-django.el
.. _Melpa: https://melpa.org
.. _flycheck: https://github.com/flycheck/flycheck
.. _anaconda-mode: https://github.com/proofit404/anaconda-mode
.. _projectile: https://github.com/bbatsov/projectile
.. _register a new project type: http://projectile.readthedocs.io/en/latest/configuration/#adding-custom-project-types
64 changes: 59 additions & 5 deletions pyenv-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

;; Author: Artem Malyshev <proofit404@gmail.com>
;; URL: https://github.com/proofit404/pyenv-mode
;; Version: 0.1.0
;; Package-Requires: ((pythonic "0.1.0"))
;; Version: 0.2.0
;; Package-Requires: ((pythonic "0.1.0") (f "0.14"))

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
Expand All @@ -26,6 +26,7 @@

;;; Code:

(require 'f)
(require 'pythonic)

(defgroup pyenv nil
Expand Down Expand Up @@ -61,6 +62,43 @@
"Read virtual environment from user input."
(completing-read "Pyenv: " (pyenv-mode-versions)))

(defun pyenv-python-version-file-exists-p (dir)
"Return t if a .python-version file found under DIR."
(f-exists? (f-expand ".python-version" dir)))

(defun pyenv-detect-project-python-version ()
"Returns the Python version found for a project.

Looks at `projectile-project-root' first, if a .python-version
file is found, returns its content.

Otherwise, `projectile-project-name' if it is an installed Python
version.

nil if none of the above is true.
"
(let* ((project-root (ignore-errors (projectile-project-root)))
(version-file (and project-root
(f-join project-root ".python-version")))
(version-file-version (and project-root
(f-exists? version-file)
(f-readable? version-file)
(string-trim
(f-read-text version-file 'utf-8))))
(project-name (projectile-project-name)))
(cond ((member version-file-version (pyenv-mode-versions))
version-file-version)
((member project-name (pyenv-mode-versions))
project-name)
(t nil))))

(defun pyenv-set-project-python-version ()
"Set Python version when opening a project."
(let ((python-version (pyenv-detect-project-python-version)))
(if python-version
(pyenv-mode-set python-version)
(pyenv-mode-unset))))

;;;###autoload
(defun pyenv-mode-set (version)
"Set python shell VERSION."
Expand All @@ -85,18 +123,34 @@
"Keymap for pyenv-mode.")

;;;###autoload
(define-minor-mode pyenv-mode
(define-minor-mode pyenv-local-mode
"Minor mode for pyenv interaction.

\\{pyenv-mode-map}"
:global t
:lighter ""
:keymap pyenv-mode-map
(if pyenv-mode
:group 'pyenv
(if pyenv-local-mode
(add-to-list 'mode-line-misc-info pyenv-mode-mode-line-format)
(setq mode-line-misc-info
(delete pyenv-mode-mode-line-format mode-line-misc-info))))

(defun pyenv-local-mode-turn-on ()
"Turn on `pyenv-local-mode' if the buffer's major mode is`python-mode'."
(when (derived-mode-p 'python-mode)
(pyenv-local-mode)))

;;;###autoload
(define-globalized-minor-mode pyenv-mode pyenv-local-mode
pyenv-local-mode-turn-on
:group 'pyenv
:after-hook
(if pyenv-mode
(with-eval-after-load 'projectile
(add-hook 'projectile-after-switch-project-hook #'pyenv-set-project-python-version))
(with-eval-after-load 'projectile
(remove-hook 'projectile-after-switch-project-hook #'pyenv-set-project-python-version))))

(provide 'pyenv-mode)

;;; pyenv-mode.el ends here