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

Jedi is "smart enough to figure out which Virtual environment you're currently using"? #199

Closed
spookylukey opened this issue Mar 17, 2022 · 11 comments

Comments

@spookylukey
Copy link

The README:

Alternatively (and preferably), use pipx to keep jedi-language-server and its dependencies isolated from your other Python dependencies. Don't worry, jedi is smart enough to figure out which Virtual environment you're currently using!

I'm sold on using pipx, but as far as I can see Jedi isn't and can't be smart enough to figure out which virtual environment to use, at least not without some help. If I use jedi-language-server on a folder, there is no way for Jedi to know which virtualenv to use - in fact I might have multiple venvs I'm using for that folder. I've attempted this using lsp-jedi for Emacs, and "jump to definition" fails to work if the item comes from a dependency located in a venv (e.g in ~/.virtualenvs/myproject).

Some clients may be configured with some mechanism for linking projects to venv, but I don't think Jedi can do it by itself.

After browsing the source code of jedi-language-server, I have found just one way to get it to work without changing the source - by creating .jedi/project.json in the project root as follows:

{
    "environment_path": "/home/me/.virtualenvs/myproject"
}

This is an almost undocumented feature of Jedi it seems - it fact it is wrongly documented as being .jedi/config.json.

I'm not sure how this is supposed to work, so:

  1. If this is the best/intended way, it would help if jedi-language-server documented this
  2. It would help if there was another way - specifically a config option that could be passed from the client. I don't think any of the existing configuration options cover this. This would enable clients to pass it using editor-appropriate settings mechanisms
@pappasam
Copy link
Owner

In general, if you have your project's virtual environment activated, jedi is smart enough to find it even if jedi isn't installed in that particular virtual environment. It does not sound like you have your virtual environment activated in your emacs environment.

That said, if you don't want to activate the environment for some reason, an existing mechanism should work for you: https://github.com/pappasam/coc-jedi#jediworkspaceextrapaths

{
  "jedi.workspace.extraPaths": ["/home/me/src/my-project/.venv/lib/python3.10/site-packages"],
}

This will be the location of the 3rd party packages from your virtual environment.

@spookylukey
Copy link
Author

@pappasam Thanks for your reply.

There is no standard way of "activating" a Python virtual environment in Emacs - there are lots of competing ones.

Could you explain the mechanism by which Jedi/jedi-language-server actually works out the virtualenv? I think I must be missing something in terms of what you are assuming.

Thanks!

@pappasam
Copy link
Owner

https://jedi.readthedocs.io/en/latest/_modules/jedi/api/environment.html?highlight=virtualenv#

I think it examines the environment for the VIRTUAL_ENV environment variable.

@pappasam
Copy link
Owner

I also don't use emacs. I run neovim from within a terminal, so my workflow entails activating my virtual environment (automatically with zsh hooks) before opening neovim.

@pappasam
Copy link
Owner

Closing due to lack of activity, hopefully this issue can serve as documentation for anyone with a similar question

@gwerbin
Copy link
Contributor

gwerbin commented Aug 26, 2022

I think even for Neovim users it would be very helpful to have some way to explicitly set the Python interpreter. I usually don't activate my venv(s), I just run .venv/bin/python ... in the terminal. I also use a GUI frontend for Neovim, so I can't rely on inheriting the environment from a shell.

Does Jedi Language Server handle any of this, or does it rely entirely on Jedi for "environment detection"? If so, is this an upstream issue, or a feature that can be added directly to Jedi LS?

I use Jedi LS instead of the "standard" Python LSP Server because I think the overall experience is better, but this has always been an annoying limitation for me. I'd be happy to at least take a look at the relevant code to see how hard it would be to add this feature.

@pappasam
Copy link
Owner

@gwerbin Your workflow may be strange to me, but fear not! I think we've got your back (please report back if I'm wrong) :)

If you install jedi-language-server in your virtual environment and specify the full path to the executable, it'll figure out where your dependencies are based on the jedi-language-server executable.

For example, if you have a virtualenv called yolo located at /home/you/project/yolo, if you /home/you/project/yolo/bin/pip install jedi-language-server, set this option to /home/you/project/yolo/bin/jedi-language-server. Things should work just fine for you.

@gwerbin
Copy link
Contributor

gwerbin commented Aug 26, 2022

@pappasam I should have also mentioned that I installed jedi-language-server with Pipx and use that version everywhere. I'd prefer to not install it separately in each env if I can avoid it!

Or have I misunderstood how Jedi LS works? Can it detect libraries from "python B" while itself running under "python A"?

@gwerbin
Copy link
Contributor

gwerbin commented Oct 26, 2022

For anyone watching this issue:

My PR #232 adding this feature just merged! And the latest Jedi Language Server release 0.38.0 should finally make this explicitly configurable using the initialization option workspace.environmentPath.

For any Neovim users, here is how I set it up in my config, with some helper functions to automatically detect the root dir, environment, and extra source paths: https://git.sr.ht/~wintershadows/dotfiles/tree/eafeb864/item/.config/nvim/lua/plugin-config/nvim-lspconfig.lua#L544-770

@chrisgrieser
Copy link

@gwerbin Thanks for this trick. I needed to dynamically determine the virtual environment, and setting it via init_option.workspace.environmentPath therefore did not cut it for me. However, on_new_config does the trick since it's a function.

This might also be worth mentioning for the documentation, as far as I can tell this is the only method to change the virtual environment during runtime.

@JulienPalard
Copy link

JulienPalard commented Dec 1, 2023

In case an emacs user lands here, I'm documenting how I'm currently doing it.

I'm installing jedi-language-server using pipx to avoid installing it in every venv.

Emacs-side I'm using envrc-mode and a .envrc file (from direnv) to "activate" my venvs at a buffer level.

This config needs to use lsp-deferred to "ensure" the variables (PATH and VIRTUAL_ENV) are set before jedi is spawned, this is the really important part.

Then jedi-language-server spots the VIRTUAL_ENV environment variable, and boom everything works.

So my setup looks like this:

(use-package envrc
  :ensure t
  :init (envrc-global-mode))

(use-package lsp-mode
  :ensure t
  :hook (((python-mode) . lsp-deferred)
         (rust-mode) .lsp)
  :commands (lsp lsp-deferred)
)

My .envrc files looks like this:

VIRTUAL_ENV=.venv
layout python3

the first line makes direnv put the venv inside a .venv/ directory instead of the default .direnv/python-VERSION which I don't like.

As a nice side effect my venvs are automatically activated/deactivated when I enter/exit a directory (from within emacs or not).

If you want to dig deeper, here's my .emacs.d/init.el and my .bashrc, at the bottom of the .bashrc there's an alias to create .envrc files, and I use python-prompt to visually see venv activations in my prompt:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants