diff --git a/modules/lang/python/+conda.el b/modules/lang/python/+conda.el deleted file mode 100644 index 50d5486f0..000000000 --- a/modules/lang/python/+conda.el +++ /dev/null @@ -1,33 +0,0 @@ -;;; lang/python/+conda.el -*- lexical-binding: t; -*- -;;;###if (featurep! +conda) - -;; Adds conda support to Doom Emacs. `conda-anaconda-home' should be the path to -;; your anaconda installation, and will be guessed from the following: -;; -;; + ~/.anaconda3 -;; + ~/.anaconda -;; + ~/usr/bin/anaconda3 -;; -;; If none of these work, you'll need to set `conda-anaconda-home' yourself. -;; -;; Once set, run M-x `conda-env-activate' to switch between environments OR turn -;; on `conda-env-autoactivate-mode' if you want it done automatically. - -(def-package! conda - :when (featurep! +conda) - :after python - :config - (unless (cl-loop for dir in (list conda-anaconda-home "/usr/bin/anaconda3" "~/.anaconda") - if (file-directory-p dir) - return (setq conda-anaconda-home dir - conda-env-home-directory dir)) - (message "Cannot find Anaconda installation")) - - ;; integration with term/eshell - (conda-env-initialize-interactive-shells) - (after! eshell (conda-env-initialize-eshell)) - - (add-hook! '(conda-postactivate-hook conda-postdeactivate-hook) - #'+python|add-conda-env-to-modeline) - - (advice-add 'anaconda-mode-bootstrap :override #'+python*anaconda-mode-bootstrap-in-remote-environments)) diff --git a/modules/lang/python/autoload/conda.el b/modules/lang/python/autoload/conda.el index 1d0a25f96..3449b6b15 100644 --- a/modules/lang/python/autoload/conda.el +++ b/modules/lang/python/autoload/conda.el @@ -16,12 +16,10 @@ executable and packages." (message "Successfully changed conda home to: %s" (abbreviate-file-name home)))) ;;;###autoload -(defun +python|add-conda-env-to-modeline () +(defun +python-conda-env () "Add conda environment string to the major mode modeline segment." - (setq mode-name - (if conda-env-current-name - (format "Py:conda:%s" conda-env-current-name) - "Python"))) + (when conda-env-current-name + (format "conda:%s" conda-env-current-name))) ;;;###autoload (defun +python*anaconda-mode-bootstrap-in-remote-environments (&optional callback) diff --git a/modules/lang/python/autoload/python.el b/modules/lang/python/autoload/python.el index c6ae3b17c..4f4030763 100644 --- a/modules/lang/python/autoload/python.el +++ b/modules/lang/python/autoload/python.el @@ -5,3 +5,25 @@ "Open the Python REPL." (interactive) (process-buffer (run-python nil t t))) + +;;;###autoload +(defun +python-version () + "Return the currently installed version of python on your system or active in +the current pipenv. + +This is not necessarily aware of env management tools like virtualenv, pyenv or +pipenv, unless those tools have modified the PATH that Emacs picked up when you +started it." + (let* ((command (if (pipenv-project-p) + "pipenv run python --version" + "python --version")) + (bin (car (split-string command " ")))) + (unless (executable-find bin) + (user-error "Couldn't find %s executable in PATH" bin)) + (with-temp-buffer + (let ((p (apply #'call-process bin nil (current-buffer) nil + (cdr (split-string command " " t)))) + (output (string-trim (buffer-string)))) + (unless (zerop p) + (user-error "'%s' failed: %s" command output)) + (nth 1 (split-string output " " t)))))) diff --git a/modules/lang/python/config.el b/modules/lang/python/config.el index 8a123d636..65f05def7 100644 --- a/modules/lang/python/config.el +++ b/modules/lang/python/config.el @@ -1,14 +1,9 @@ ;;; lang/python/config.el -*- lexical-binding: t; -*- -(defvar +python-pyenv-root nil - "The path to pyenv's root directory. This is automatically set when `python' -is loaded.") - -(defvar +python-pyenv-versions nil - "Available versions of python in pyenv.") - -(defvar-local +python-current-version nil - "The currently active pyenv version.") +(defvar +python-mode-name-functions '(+python-version) + "A list of functions to retrieve a version or environment string from. The +first to return non-nil will have its result appended to the python-mode +`mode-name' and displayed in the mode-line.") ;; @@ -22,7 +17,7 @@ is loaded.") python-indent-guess-indent-offset-verbose nil python-shell-interpreter "python") :config - (set-env! "PYTHONPATH" "PYENV_ROOT") + (set-env! "PYTHONPATH" "PYENV") (set-electric! 'python-mode :chars '(?:)) (set-repl-handler! 'python-mode #'+python/repl) @@ -44,7 +39,11 @@ is loaded.") :for "for" :return "return" :yield "yield") - (when (executable-find "ipython") + (define-key python-mode-map (kbd "DEL") nil) ; interferes with smartparens + (sp-with-modes 'python-mode + (sp-local-pair "'" nil :unless '(sp-point-before-word-p sp-point-after-word-p sp-point-before-same-p))) + + (when (featurep! +ipython) (setq python-shell-interpreter "ipython" python-shell-interpreter-args "-i --simple-prompt --no-color-info" python-shell-prompt-regexp "In \\[[0-9]+\\]: " @@ -55,45 +54,21 @@ is loaded.") python-shell-completion-string-code "';'.join(get_ipython().Completer.all_completions('''%s'''))\n")) - ;; Version management with pyenv (defun +python|add-version-to-modeline () "Add version string to the major mode in the modeline." (setq mode-name - (if +python-current-version - (format "Python %s" +python-current-version) + (if-let* ((result (run-hook-with-args-until-success '+python-mode-name-functions))) + (format "Python %s" result) "Python"))) - (add-hook 'python-mode-hook #'+python|add-version-to-modeline) - - (if (not (executable-find "pyenv")) - (setq-default +python-current-version (string-trim (shell-command-to-string "python --version 2>&1 | cut -d' ' -f2"))) - (setq +python-pyenv-root (string-trim (shell-command-to-string "pyenv root")) - +python-pyenv-versions (split-string (shell-command-to-string "pyenv versions --bare") "\n" t)) - - (defun +python|detect-pyenv-version () - "Detect the pyenv version for the current project and set the relevant -environment variables." - (when-let* ((version-str (shell-command-to-string "PYENV_VERSION= python --version 2>&1 | cut -d' ' -f2"))) - (setq version-str (string-trim version-str) - +python-current-version version-str) - (let ((pyenv-current-path (concat +python-pyenv-root "/versions/" version-str))) - (when (file-directory-p pyenv-current-path) - (setq pythonic-environment pyenv-current-path))) - (when (member version-str +python-pyenv-versions) - (setenv "PYENV_VERSION" version-str)))) - (add-hook 'python-mode-hook #'+python|detect-pyenv-version)) - - (define-key python-mode-map (kbd "DEL") nil) ; interferes with smartparens - (sp-with-modes 'python-mode - (sp-local-pair "'" nil :unless '(sp-point-before-word-p sp-point-after-word-p sp-point-before-same-p)))) + (add-hook 'python-mode-hook #'+python|add-version-to-modeline)) (def-package! anaconda-mode - :after python + :hook python-mode :init (setq anaconda-mode-installation-directory (concat doom-etc-dir "anaconda/") anaconda-mode-eldoc-as-single-line t) :config - (add-hook 'python-mode-hook #'anaconda-mode) (add-hook 'anaconda-mode-hook #'anaconda-eldoc-mode) (set-company-backend! 'python-mode '(company-anaconda)) (set-popup-rule! "^\\*anaconda-mode" :select nil) @@ -111,6 +86,8 @@ environment variables." (add-hook! 'python-mode-hook (add-hook 'kill-buffer-hook #'+python|auto-kill-anaconda-processes nil t)) + (when (featurep 'evil) + (add-hook 'anaconda-mode-hook #'evil-normalize-keymaps)) (map! :map anaconda-mode-map :localleader :prefix "f" @@ -123,13 +100,14 @@ environment variables." (def-package! nose :commands nose-mode - :preface - (defvar nose-mode-map (make-sparse-keymap)) - :init - (associate! nose-mode :match "/test_.+\\.py$" :modes (python-mode)) + :preface (defvar nose-mode-map (make-sparse-keymap)) + :init (associate! nose-mode :match "/test_.+\\.py$" :modes (python-mode)) :config (set-popup-rule! "^\\*nosetests" :size 0.4 :select nil) (set-yas-minor-mode! 'nose-mode) + (when (featurep 'evil) + (add-hook 'nose-mode-hook #'evil-normalize-keymaps)) + (map! :map nose-mode-map :localleader :prefix "t" @@ -143,9 +121,59 @@ environment variables." ;; -;; Evil integration +;; Environment management ;; -(when (featurep! :feature evil +everywhere) - (add-hook! '(anaconda-mode-hook nose-mode-hook) - #'evil-normalize-keymaps)) +(def-package! pipenv + :commands pipenv-project-p + :hook (python-mode . pipenv-mode)) + + +(def-package! pyenv-mode + :when (featurep! +pyenv) + :after python + :config + (pyenv-mode +1) + (add-to-list '+python-mode-name-functions #'pyenv-mode-version nil #'eq)) + + +(def-package! pyvenv + :when (featurep! +pyvenv) + :after python + :config + (defun +python-current-pyvenv () pyvenv-virtual-env-name) + (add-to-list '+python-mode-name-functions #'+python-current-pyvenv nil #'eq)) + + +(def-package! conda + :when (featurep! +conda) + :after python + :config + ;; Adds conda support to Doom Emacs. `conda-anaconda-home' should be the path + ;; to your anaconda installation, and will be guessed from the following: + ;; + ;; + ~/.anaconda3 + ;; + ~/.anaconda + ;; + ~/.miniconda + ;; + ~/usr/bin/anaconda3 + ;; + ;; If none of these work, you'll need to set `conda-anaconda-home' yourself. + ;; + ;; Once set, run M-x `conda-env-activate' to switch between environments OR + ;; turn on `conda-env-autoactivate-mode' if you want it done automatically. + (unless (cl-loop for dir in (list conda-anaconda-home + "~/.anaconda" + "~/.miniconda" + "/usr/bin/anaconda3") + if (file-directory-p dir) + return (setq conda-anaconda-home dir + conda-env-home-directory dir)) + (message "Cannot find Anaconda installation")) + + ;; integration with term/eshell + (conda-env-initialize-interactive-shells) + (after! eshell (conda-env-initialize-eshell)) + + (add-to-list '+python-mode-name-functions #'+python-conda-env nil #'eq) + + (advice-add 'anaconda-mode-bootstrap :override #'+python*anaconda-mode-bootstrap-in-remote-environments)) diff --git a/modules/lang/python/packages.el b/modules/lang/python/packages.el index 0b67053d2..24e5b85af 100644 --- a/modules/lang/python/packages.el +++ b/modules/lang/python/packages.el @@ -1,12 +1,21 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/python/packages.el -;; requires: python jedi setuptools +;; requires: python setuptools (package! nose) (package! pip-requirements) + +;; Environmet management +(package! pipenv) +(when (featurep! +pyenv) + (package! pyenv-mode)) +(when (featurep! +pyvenv) + (package! pyvenv-mode)) +(when (featurep! +conda) + (package! conda)) + +;; Programming environment (when (package! anaconda-mode) (when (featurep! :completion company) (package! company-anaconda))) -(when (featurep! +conda) - (package! conda))