228 lines
9.3 KiB
EmacsLisp
228 lines
9.3 KiB
EmacsLisp
;;; python-settings.el --- Python settings -*- mode: emacs-lisp; lexical-binding: t -*-
|
||
|
||
;;; Commentary:
|
||
|
||
;; Requirements:
|
||
;; program: python
|
||
;; Emacs packages: anaconda-mode company-anaconda sphinx-doc
|
||
|
||
;; Language Server Support, Emacs built-in package `eglot'
|
||
;; https://github.com/joaotavora/eglot
|
||
;; connects to LSP
|
||
;; activate with `eglot'
|
||
;; after changing configurations run `eglot-reconnect'
|
||
;; follow documentation at point `eldoc-doc-buffer' / `display-local-help'
|
||
|
||
;; Language Server Protocol (LSP), python package `python-lsp-server'
|
||
;; https://github.com/python-lsp/python-lsp-server
|
||
;; install via: pip install "python-lsp-server[all]"
|
||
;; arch linux: extra/python-lsp-server
|
||
;; - pycodestyle (formerly called pep8) - Python style guide checker
|
||
;; https://pycodestyle.pycqa.org/en/latest/
|
||
;; to ignore violations see:
|
||
;; - https://flake8.pycqa.org/en/latest/user/violations.html
|
||
;; - https://flake8.pycqa.org/en/latest/user/configuration.html#configuration
|
||
;; arch linux: extra/python-pycodestyle
|
||
;; config via ~/.config/pycodestyle
|
||
;; - pylsp-mypy: pylsp plugin, type checking for Python >=3.8
|
||
;; https://mypy-lang.org/
|
||
;; pip install pylsp-mypy
|
||
;; arch linux: aur/python-pylsp-mypy
|
||
|
||
;; Syntax Checking, Emacs built-in package `flymake'
|
||
;; (flymake-running-backends) -> (eglot-flymake-backend)
|
||
|
||
;;; Code:
|
||
(use-package python
|
||
:delight (python-mode "Py") ;; "Py " "\u1F15F"
|
||
:commands (python-mode)
|
||
:bind (:map python-mode-map
|
||
;; swap C-u C-c C-c with C-c C-c, so C-c C-c sends __main__
|
||
("C-c C-c" . my-python-shell-send-buffer-w-main)
|
||
("C-u C-c C-c" . my-python-shell-send-buffer)
|
||
("M-m m c c" . my-python-execute-file)
|
||
("M-m m c C" . my-python-execute-file-switch)
|
||
("M-m m s b" . my-python-shell-send-buffer-w-main))
|
||
:config
|
||
(setq python-indent-offset 2) ; python-indent is obsolete since 24.3; use python-indent-offset instead.
|
||
(setq python-indent-guess-indent-offset nil) ; default t. nil to use the config value python-indent-offset
|
||
(setq python-indent-guess-indent-offset-verbose nil) ; hide message like: Can’t guess python-indent-offset, using defaults: 2
|
||
;;(setq python-shell-interpreter "ipython")
|
||
;;(setq python-shell-interpreter-args "-i")
|
||
|
||
(defun my-pyenv-executable-find (command)
|
||
"Find executable taking pyenv shims into account.
|
||
If the executable is a system executable and not in the same path
|
||
as the pyenv version then also return nil. This works around https://github.com/pyenv/pyenv-which-ext
|
||
"
|
||
(if (executable-find "pyenv")
|
||
(progn
|
||
(let ((pyenv-string (shell-command-to-string (concat "pyenv which " command)))
|
||
(pyenv-version-names (split-string (string-trim (shell-command-to-string "pyenv version-name")) ":"))
|
||
(executable nil)
|
||
(i 0))
|
||
(if (not (string-match "not found" pyenv-string))
|
||
(while (and (not executable)
|
||
(< i (length pyenv-version-names)))
|
||
(if (string-match (elt pyenv-version-names i) (string-trim pyenv-string))
|
||
(setq executable (string-trim pyenv-string)))
|
||
(if (string-match (elt pyenv-version-names i) "system")
|
||
(setq executable (string-trim (executable-find command))))
|
||
(setq i (1+ i))))
|
||
executable))
|
||
(executable-find command)))
|
||
|
||
(defun my-python-execute-file (arg)
|
||
"Execute a python script in a shell."
|
||
(interactive "P")
|
||
;; set compile command to buffer-file-name
|
||
;; universal argument put compile buffer in comint mode
|
||
(let ((universal-argument t)
|
||
(compile-command (format "%s %s"
|
||
(my-pyenv-executable-find python-shell-interpreter)
|
||
(shell-quote-argument (file-name-nondirectory buffer-file-name)))))
|
||
(if arg
|
||
(call-interactively 'compile)
|
||
(compile compile-command t)
|
||
(with-current-buffer (get-buffer "*compilation*")
|
||
(inferior-python-mode)))))
|
||
|
||
(defun my-python-execute-file-switch (arg)
|
||
"Execute a python script in a shell and switch to the shell buffer in `insert state'."
|
||
(interactive "P")
|
||
(my-python-execute-file arg)
|
||
(switch-to-buffer-other-window "*compilation*")
|
||
(end-of-buffer)
|
||
(evil-insert-state))
|
||
|
||
(defun my-python-shell-send-buffer (&optional send-main)
|
||
"Send buffer content to shell."
|
||
(interactive)
|
||
(let ((python-mode-hook nil)) ;; TODO: why?
|
||
(python-shell-send-buffer send-main)))
|
||
|
||
(defun my-python-shell-send-buffer-w-main ()
|
||
"Send buffer content to shell with main."
|
||
(interactive)
|
||
(my-python-shell-send-buffer t))
|
||
|
||
(defadvice run-python
|
||
(after my-advice-run-python activate)
|
||
"set flag to allow exit without query on any active python
|
||
process."
|
||
(set-process-query-on-exit-flag ad-return-value nil))
|
||
|
||
(defun my-python-shell-send-buffer-w-args (args)
|
||
"Send buffer content to shell with main and arguments."
|
||
(interactive "sPython arguments: ")
|
||
(let ((source-buffer (current-buffer)))
|
||
(with-temp-buffer
|
||
(insert "import sys; sys.argv = '''" args "'''.split()\n")
|
||
(insert-buffer-substring source-buffer)
|
||
(my-python-shell-send-buffer-w-main))))
|
||
)
|
||
|
||
;; needs system package python-jedi
|
||
;; (use-package anaconda-mode ;; works with company-mode via company-anaconda
|
||
;; :after python
|
||
;; :delight (anaconda-mode "A") ;; \u24B6 a
|
||
;; :bind (([remap anaconda-mode-show-doc] . my-anaconda-mode-show-doc)) ;; M-?
|
||
;; :hook (python-mode
|
||
;; (python-mode . anaconda-eldoc-mode)) ;; if cursor is between function's parenthesis show parameter in echo area
|
||
;; :config
|
||
;; (setq anaconda-mode-installation-directory (concat user-cache-directory "anaconda-mode"))
|
||
;; ;; compared to `anaconda-mode-show-doc' using `my-anaconda-mode-show-doc-callback'
|
||
;; (defun my-anaconda-mode-show-doc ()
|
||
;; "Show documentation for context at point."
|
||
;; (interactive)
|
||
;; ;;(anaconda-mode-call "show_doc" 'anaconda-mode-show-doc-callback) ;; removed
|
||
;; (anaconda-mode-call "show_doc" 'my-anaconda-mode-show-doc-callback) ;; instead
|
||
;; )
|
||
;; ;; compared to `anaconda-mode-show-doc-callback' no change of focus
|
||
;; (defun my-anaconda-mode-show-doc-callback (result)
|
||
;; "Process view doc RESULT."
|
||
;; (if (> (length result) 0)
|
||
;; (if (and anaconda-mode-use-posframe-show-doc
|
||
;; (require 'posframe nil 'noerror)
|
||
;; (posframe-workable-p))
|
||
;; (anaconda-mode-documentation-posframe-view result)
|
||
;; ;;(pop-to-buffer (anaconda-mode-documentation-view result) t) ;; removed
|
||
;; (anaconda-mode-documentation-view result) ;; instead
|
||
;; )
|
||
;; (message "No documentation available")))
|
||
;; )
|
||
|
||
;; (use-package company-anaconda
|
||
;; :after (python anaconda-mode company)
|
||
;; :bind (([remap anaconda-mode-complete] . company-anaconda))
|
||
;; :config
|
||
;; (add-to-list 'company-backends '(company-anaconda :with company-capf))) ;; to see anaconda-mode completions together with ones comes from inferior python process
|
||
|
||
;; https://melpa.org/#/sphinx-doc
|
||
;; https://github.com/naiquevin/sphinx-doc.el
|
||
;; enable the sphinx-doc-mode and bind the interactive function sphinx-doc to C-c M-d.
|
||
(use-package sphinx-doc
|
||
:delight (sphinx-doc-mode "Sph") ;; \u24C8 s
|
||
:hook (python-mode . sphinx-doc-mode))
|
||
|
||
|
||
|
||
|
||
;; jupyter repl
|
||
;; optional markdown-mode
|
||
;; optional company-mode
|
||
;; emacs-websocket
|
||
;; simple-httpd
|
||
;; zmq
|
||
(use-package jupyter
|
||
:commands (;; launch a new local kernel and displays a REPL buffer
|
||
jupyter-run-repl
|
||
;; connect to an existing kernel using the kernel’s connection
|
||
;; file, which is supplied by the user, and displays a REPL buffer
|
||
jupyter-connect-repl
|
||
;; associate the buffer to an existing emacs-jupyter REPL or create a new one choosing the appropriate Jupyter kernel
|
||
jupyter-repl-associate-buffer)
|
||
:init
|
||
(setq jupyter-use-zmq nil)
|
||
:config
|
||
(add-to-list 'org-structure-template-alist '("py" . "src jupyter-python"))
|
||
;; emacs-jupyter will always use the Python kernel found on startup
|
||
(defun my-jupyter-refresh-kernelspecs ()
|
||
"Refresh Jupyter kernelspecs"
|
||
(interactive)
|
||
(jupyter-available-kernelspecs t))
|
||
)
|
||
|
||
;; https://github.com/astoff/code-cells.el
|
||
;; .ipynb
|
||
(use-package code-cells
|
||
:commands (code-cells-mode code-cells-convert-ipynb)
|
||
;;:mode ("\\.ipynb\\'" . code-cells-mode)
|
||
:hook (code-cells-mode . code-cells-convert-ipynb)
|
||
:config
|
||
(setq code-cells-convert-ipynb-style '(("pandoc" "--to" "ipynb" "--from" "org")
|
||
("pandoc" "--to" "org" "--from" "ipynb")
|
||
(lambda () #'org-mode)))
|
||
)
|
||
|
||
(use-package ox-ipynb
|
||
:after ox)
|
||
|
||
;; https://millejoh.github.io/emacs-ipython-notebook/
|
||
;; requires: anaphora dash deferred polymode request websocket with-editor
|
||
(use-package ein
|
||
:commands (ein:run ein:jupyter-server-start ein:notebooklist-open)
|
||
:init
|
||
:config
|
||
(require 'ein-jupyter)
|
||
(require 'ein-notebook)
|
||
(setq ein:output-area-inlined-images t)
|
||
;; ob-ein
|
||
(with-eval-after-load 'org
|
||
(add-to-list 'org-babel-load-languages '(ein . t)))
|
||
;; also `org-babel-load-languages' ?
|
||
)
|
||
|
||
(provide 'python-settings)
|
||
;;; python-settings.el ends here
|