diff --git a/ha-programming-python.org b/ha-programming-python.org index 2aea760..eaeaacc 100644 --- a/ha-programming-python.org +++ b/ha-programming-python.org @@ -136,7 +136,14 @@ Next, after reading David Vujic’s [[https://davidvujic.blogspot.com/2025/03/ar # c.InteractiveShellApp.exec_lines = ['%autoreload 2'] #+END_SRC -** Virtual Environment +** Isolated Python Environments +While the Python community (and my work at my company) had difficulty transitioning from Python 2 to 3, I often run into issues needing a particular Python version and modules. After playing around with different approaches, I’m finding: + * Docker environments are nicely isolated, but annoying to work from outside the container + * The Builtin =venv= is works well for different library modules, but not for different versions + * The =pyenv= deals with different Python versions, but is overkill for library isolation + +While the [[https://github.com/marcwebbie/auto-virtualenv][auto-virtualenv]] project attempts to resolve this, I’m using the [[file:ha-programming.org::*Virtual Environments with direnv][direnv project]] abstraction for situations where I need project-specific isolation in more than just Python. +*** Virtual Environments Use the built-in module, venv, to create isolated Python environments for specific projects, enabling you to manage dependencies separately. Create a virtual environment, either in the project’s directory, or in a global spot: @@ -160,8 +167,8 @@ Now, do what you need to do with this isolation: #+BEGIN_SRC sh :tangle no pip install -r test-requirements.txt #+END_SRC -** Virtual Environment with new Python Version -Pyenv is a tool for managing multiple versions of Python on your machine, allowing you to switch between them easily. On a Mac, installed it via Homebrew: +*** Managing Python Versions +[[https://github.com/pyenv/pyenv][Pyenv]] is a tool for managing multiple versions of Python on your machine, allowing you to switch between them easily (see [[https://realpython.com/intro-to-pyenv/][this essay]]). On a Mac, installed it via Homebrew: #+BEGIN_SRC sh brew install readline xz @@ -213,16 +220,55 @@ Also, you need the following in your =~/.config/direnv/direnvrc= file (which I h fi } #+end_src -** Editing Python Code -Let’s integrate this [[https://github.com/wbolster/evil-text-object-python][Python support for evil-text-object]] project: -#+begin_src emacs-lisp - (when (fboundp 'evil-define-text-object) - (use-package evil-text-object-python - :hook (python-mode . evil-text-object-python-add-bindings))) -#+end_src -This allows me to delete a Python “block” using ~dal~. -** Docker Environment -Docker really allows you to isolate your project's environment. The downside is that you are using Docker and probably a bloated container. On my work laptop, a Mac, this creates a behemoth virtual machine that immediately spins the fans like a wind tunnel. + +Tell Emacs about [[https://github.com/pythonic-emacs/pyenv-mode][pyenv-mode]]: + +#+BEGIN_SRC emacs-lisp + (use-package pyenv-mode + :config + (defun setup-pyenv () + "Pyenv." + (setenv "WORKON_HOME" "~/.pyenv/versions") + (pyenv-mode +1))) +#+END_SRC + +Now specify the =pyenv= Python version by calling [[help:pyenv-mode-set][pyenv-mode-set]]: + +#+begin_example + M-x pyenv-mode-set +#+end_example + +When you run inferior Python processes (like =run-python=), the process will start inside the specified Python installation. You can unset the current version with: + +#+begin_example + M-x pyenv-mode-unset +#+end_example + +Or, we can do it automatically when we get into a project (if the project has a =.python-version= file): + +#+BEGIN_SRC emacs-lisp + (use-package pyenv-mode + :config + (defun project-pyenv-mode-set (&rest _) + "Set pyenv version matching project name." + (let* ((filename (thread-first + (project-current) + (project-root) + (file-name-concat ".python-version"))) + (version (when (file-exists-p filename) + (with-temp-buffer + (insert-file-contents filename) + (buffer-string))))) + (when version + (pyenv-mode-set version) + (pyenv-mode-unset)))) + + ;; Either set/unset the pyenv version whenever changing tabs: + (add-hook 'tab-bar-tab-post-select-functions 'project-pyenv-mode-set)) +#+END_SRC + +*** Docker Environment +Docker allows you to isolate your project's environment. The downside is that you are using Docker and probably a bloated container. On my work laptop, a Mac, this creates a behemoth virtual machine that immediately spins the fans like a wind tunnel. But, but... think of the dependencies! @@ -236,6 +282,14 @@ Your project's =.envrc= file would contain something like: container_layout #+end_src +** Editing Python Code +Let’s integrate this [[https://github.com/wbolster/evil-text-object-python][Python support for evil-text-object]] project: +#+begin_src emacs-lisp + (when (fboundp 'evil-define-text-object) + (use-package evil-text-object-python + :hook (python-mode . evil-text-object-python-add-bindings))) +#+end_src +This allows me to delete a Python “block” using ~dal~. ** Unit Tests #+begin_src emacs-lisp (use-package python-pytest @@ -267,9 +321,22 @@ The [[https://elpy.readthedocs.io/en/latest/introduction.html][Elpy Project]] ex #+BEGIN_SRC emacs-lisp (use-package elpy - :ensure t :init - (elpy-enable)) + (advice-add 'python-mode :before 'elpy-enable) + :config + ;; (elpy-enable) + (setq elpy-test-runner 'elpy-test-pytest-runner) + (setq elpy-formatter 'black) + (setq elpy-shell-echo-input nil) + (setq elpy-modules (delq 'elpy-module-flymake elpy-modules))) +#+END_SRC + +After we’ve loaded the Company section, we can add jedi to the list of completions: + +#+BEGIN_SRC emacs-lisp + (use-package elpy + :after company-mode + :config (add-to-list 'company-backends 'company-jedi)) #+END_SRC Let’s expand our =major-mode-hydra= with some extras: diff --git a/ha-programming.org b/ha-programming.org index 8faa9d6..cdbae45 100644 --- a/ha-programming.org +++ b/ha-programming.org @@ -670,26 +670,26 @@ Emacs has two LSP projects, and while I have used [[LSP Mode]], but since I don :config (global-set-key (kbd "s-m") 'lsp) - (ha-local-leader :keymaps 'prog-mode-map - "w" '(:ignore t :which-key "lsp") - "l" '(:ignore t :which-key "lsp") - "ws" '("start" . lsp)) + ;; (ha-local-leader :keymaps 'prog-mode-map + ;; "w" '(:ignore t :which-key "lsp") + ;; "l" '(:ignore t :which-key "lsp") + ;; "ws" '("start" . lsp)) - ;; The following leader-like keys, are only available when I have - ;; started LSP, and is an alternate to Command-m: - :general - (:states 'normal :keymaps 'lsp-mode-map - ", w r" '("restart" . lsp-reconnect) - ", w b" '("events" . lsp-events-buffer) - ", w e" '("errors" . lsp-stderr-buffer) - ", w q" '("quit" . lsp-shutdown) - ", w Q" '("quit all" . lsp-shutdown-all) + ;; ;; The following leader-like keys, are only available when I have + ;; ;; started LSP, and is an alternate to Command-m: + ;; :general + ;; (:states 'normal :keymaps 'lsp-mode-map + ;; ", w r" '("restart" . lsp-reconnect) + ;; ", w b" '("events" . lsp-events-buffer) + ;; ", w e" '("errors" . lsp-stderr-buffer) + ;; ", w q" '("quit" . lsp-shutdown) + ;; ", w Q" '("quit all" . lsp-shutdown-all) - ", l r" '("rename" . lsp-rename) - ", l f" '("format" . lsp-format) - ", l a" '("actions" . lsp-code-actions) - ", l i" '("imports" . lsp-code-action-organize-imports) - ", l d" '("doc" . lsp-lookup-documentation)) + ;; ", l r" '("rename" . lsp-rename) + ;; ", l f" '("format" . lsp-format) + ;; ", l a" '("actions" . lsp-code-actions) + ;; ", l i" '("imports" . lsp-code-action-organize-imports) + ;; ", l d" '("doc" . lsp-lookup-documentation)) :hook ((lsp-mode . lsp-enable-which-key-integration))) #+end_src diff --git a/zshell.org b/zshell.org index 78226a1..a772385 100644 --- a/zshell.org +++ b/zshell.org @@ -416,15 +416,25 @@ Favorite feature is the [[https://iterm2.com/documentation-status-bar.html][Stat Currently, I show the currently defined Kube namespace. #+BEGIN_SRC zsh + function iterm2_python_version() { + echo $(pyenv version-name):$(echo "$VIRTUAL_ENV" | sed " + s|^$HOME|~| + s|^~/src/wpc-gerrit.inday.io/|| + s|^~/work/|| + s|^~/.venv/|| + s|/\.venv$|| + s|\.venv$||") + } + function iterm2_print_user_vars() { - # iterm2_set_user_var kubecontext $($ yq '.users[0].name' ~/.kube/config):$(kubectl config view --minify --output 'jsonpath={..namespace}') + # iterm2_set_user_var kubecontext $($ yq '.users[0].name' ~/.kube/config):$(kubectl config view --minify --output 'jsonpath={..namespace}') - # Correct version: - # iterm2_set_user_var kubecontext $(kubectl config current-context):$(kubectl config view --minify --output 'jsonpath={..namespace}') - # Faster version: - iterm2_set_user_var kubecontext $(awk '/^current-context:/{print $2;exit;}' <~/.kube/config) + # Correct version: + # iterm2_set_user_var kubecontext $(kubectl config current-context):$(kubectl config view --minify --output 'jsonpath={..namespace}') + # Faster version: + iterm2_set_user_var kubecontext $(awk '/^current-context:/{print $2;exit;}' <~/.kube/config) - iterm2_set_user_var pycontext "$(pyenv version-name):$(echo $VIRTUAL_ENV | sed 's/.*.venv\///')" + iterm2_set_user_var pycontext $(iterm2_python_version) } #+END_SRC