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

[WIP/RFC] Improve selection of environments #836

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
17 changes: 17 additions & 0 deletions autoload/jedi.vim
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ let s:default_settings = {
\ 'quickfix_window_height': 10,
\ 'force_py_version': "'auto'",
\ 'smart_auto_mappings': 0,
\ 'use_environment': "'auto'",
\ 'environment_paths': '[]',
\ 'use_tag_stack': 1
\ }

Expand Down Expand Up @@ -294,6 +296,14 @@ call jedi#init_python() " Might throw an error.
" ------------------------------------------------------------------------
" functions that call python code
" ------------------------------------------------------------------------
function! jedi#use_environment(...) abort
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to set_environment?

PythonJedi jedi_vim.set_environment(*vim.eval('a:000'))
endfunction

function! jedi#use_environment_for_buffer(env) abort
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.
There should also be setters (and getters) for tab- and window-local settings.

PythonJedi jedi_vim.set_environment(*vim.eval('[env, bufnr("%")]'))
endfunction

function! jedi#goto() abort
PythonJedi jedi_vim.goto(mode="goto")
endfunction
Expand Down Expand Up @@ -750,6 +760,13 @@ function! jedi#setup_completion() abort
endif
endfunction

" TODO: fallback to completing files (i.e. return known envs first, then
" files?!).
function! jedi#complete_environments(argl, cmdl, pos) abort
PythonJedi jedi_vim.complete_environments()
EOF
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove EOF.

endfunction

"PythonJedi jedi_vim.jedi.set_debug_function(jedi_vim.print_to_stdout, speed=True, warnings=False, notices=False)
"PythonJedi jedi_vim.jedi.set_debug_function(jedi_vim.print_to_stdout)

Expand Down
16 changes: 16 additions & 0 deletions plugin/jedi.vim
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,22 @@ function! s:jedi_debug_info()
call jedi#debug_info()
endfunction
command! -nargs=0 -bar JediDebugInfo call s:jedi_debug_info()

" Transfer bang into current buffer number.
" TODO: use window instead!?
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above - should support global, tab, window and buffer.

function! s:use_environment(bang, env) abort
let bufnr = a:bang ? +bufnr('%') : 0
if a:env ==# 'auto'
let env = ''
elseif a:env[-4:] ==# ' (*)'
let env = a:env[:-5]
else
let env = a:env
endif
return jedi#use_environment(env, bufnr)
endfunction
command! -bang -nargs=? -complete=custom,jedi#complete_environments JediUseEnvironment :call s:use_environment(<bang>0, <q-args>)

command! -nargs=0 -bang JediClearCache call jedi#clear_cache(<bang>0)

" vim: set et ts=4:
71 changes: 53 additions & 18 deletions pythonx/jedi_vim.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,49 +211,84 @@ def wrapper(*args, **kwargs):
return func_receiver


# XXX: might use lru cache, given that you can configure it per buffer?!
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably all the caching (and invalidation) should be done by Jedi.

current_environment = (None, None)


def set_environment(executable=None, bufnr=None):
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs support for tab, window, and bufnr.
Could be scope.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you can do that, I don't think this is what most users need/want. The scope most users want to use is probably global, but I'm not against it - I'm just saying that you're probably doing more work than necessary if you use a bufnr scope. :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it is not really necessary, but a good example for getting the scoping right from the beginning.
More important would be a tab-local setting then I guess.
This is kind of useful in an IDE-like environment where you would work on different projects/venvs in parallel - but yes, it is a bit constructed.

global current_environment

# print(repr(executable), repr(bufnr))

if not executable: # "unset"
executable = 'auto'
environment = None
else:
try:
# XXX: create_environment is not deterministic
# (https://github.com/davidhalter/jedi/issues/1155)
# jedi.create_environment(executable, safe=False)
environment = jedi.api.environment.Environment(executable)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for information, I had to comment this line and use environment = jedi.create_environment(executable, safe=False) instead to get it working.

I'm using python3.7 with pyenv.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Fenkiou
Thanks for trying this.
Did you get an error before? ("'executable=%s is not supported: %s.'")
If so, what was the message?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope the message was this one:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/Users/alex/.vim/repos/github.com/davidhalter/jedi-vim/pythonx/jedi_vim.py", line 177, in set_environment
    environment = jedi.api.environment.Environment(executable=executable)
TypeError: __init__() missing 1 required positional argument: 'path'

But in fact, I didn't run git submodule update when checking out your branch, everything is working correctly without any change! Sorry for the noise.

except jedi.InvalidPythonEnvironment as exc:
echo_highlight('executable=%s is not supported: %s.' % (
executable, str(exc)))
return
bufnr = int(bufnr)
if bufnr:
vim_command("call setbufvar(%d, 'jedi_use_environment', %r)" % (
bufnr, executable))
else:
vim_command("let g:jedi#use_environment = %r" % executable)
current_environment = (executable, environment)


def get_environment(use_cache=True):
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add scope here then, too. But it would default to preferring bufnr over window over tab over globals.

global current_environment

vim_force_python_version = vim_eval("g:jedi#force_py_version")
if use_cache and vim_force_python_version == current_environment[0]:
use_environment = vim_eval(
"get(b:, 'jedi_use_environment', get(g:, 'jedi#use_environment', ''))")
if use_cache and use_environment == current_environment[0]:
return current_environment[1]

environment = None
if vim_force_python_version == "auto":
if use_environment == "auto":
environment = jedi.api.environment.get_cached_default_environment()
else:
force_python_version = vim_force_python_version
if '0000' in force_python_version or '9999' in force_python_version:
# It's probably a float that wasn't shortened.
try:
force_python_version = "{:.1f}".format(float(force_python_version))
except ValueError:
pass
elif isinstance(force_python_version, float):
force_python_version = "{:.1f}".format(force_python_version)

try:
environment = jedi.get_system_environment(force_python_version)
# XXX: create_environment is not deterministic
# (https://github.com/davidhalter/jedi/issues/1155)
# jedi.create_environment(executable, safe=False)
environment = jedi.api.environment.Environment(use_environment)
except jedi.InvalidPythonEnvironment as exc:
environment = jedi.api.environment.get_cached_default_environment()
echo_highlight(
"force_python_version=%s is not supported: %s - using %s." % (
vim_force_python_version, str(exc), str(environment)))
'use_environment=%s is not supported: %s - using %s.' % (
use_environment, str(exc), str(environment)))

current_environment = (vim_force_python_version, environment)
current_environment = (use_environment, environment)
return environment


def get_known_environments():
"""Get known Jedi environments."""
envs = list(jedi.api.environment.find_virtualenvs())
paths = vim_eval("g:jedi#environment_paths")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If paths is empty, it should probably be the current directory. Or does that not make sense?

envs = list(jedi.api.environment.find_virtualenvs(paths=paths, safe=False))
envs.extend(jedi.api.environment.find_system_environments())
return envs


def complete_environments():
envs = get_known_environments()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Especially since the latest changes we need to cache those, otherwise this is going to be very very slow.

try:
current_executable = current_environment[1].executable
except AttributeError:
current_executable = None
vim.command("return '%s'" % '\n'.join(
('%s (*)' if env.executable == current_executable else '%s') % (
env.executable
) for env in envs))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: display more information (after the name, similar to the currently selected one), e.g. the version.



@catch_and_print_exceptions
def get_script(source=None, column=None):
jedi.settings.additional_dynamic_modules = [
Expand Down