diff --git a/.github/workflows/test.yml b/.github/workflows/activation.yml similarity index 93% rename from .github/workflows/test.yml rename to .github/workflows/activation.yml index 39d0963..c06732a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/activation.yml @@ -25,7 +25,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v7 with: - python-version: "3.12" # Vim on Ubuntu supports only 3.12 + python-version: "3.14" # Vim on Ubuntu supports only 3.14 - name: Set up tools run: | @@ -66,4 +66,5 @@ jobs: -c "call assert_equal('${{ matrix.tool }}', virtualenv#state('virtualenv_type'))" \ -c "call assert_equal(${MATRIX_KIND_CODE}, virtualenv#state('virtualenv_internal'))" \ -c "call assert_equal('${VIM_VIRTUALENV_TARGET}', virtualenv#state('virtualenv_directory'))" \ + -c "call assert_equal('${VIM_VIRTUALENV_TARGET}', py3eval('sys.prefix'))" \ -c "call g:ExitTest()" diff --git a/.github/workflows/dyn-stable.yml b/.github/workflows/dyn-stable.yml new file mode 100644 index 0000000..2e8c1bd --- /dev/null +++ b/.github/workflows/dyn-stable.yml @@ -0,0 +1,73 @@ +name: Test dynamic stable ABI virtual environment activation + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + test: + runs-on: ubuntu-latest + container: fedora:45 + timeout-minutes: 10 + + defaults: + run: + shell: bash + + strategy: + fail-fast: false + matrix: + tool: [ "venv", "virtualenv" ] + kind: [ "internal", "external" ] + system-python: [ "3.14", "3.12" ] + venv-python: [ "3.14", "3.12" ] + + steps: + - name: Set up tools + run: | + dnf install -y git python3.12 python3.14 python3-virtualenv + + - name: Checkout repository + uses: actions/checkout@v7 + + - name: Set up Python + run: | + export VIM_VIRTUALENV_PYTHON_BIN="${RUNNER_TEMP}/python-bin" + mkdir -p "${VIM_VIRTUALENV_PYTHON_BIN}" + ln -s "$(command -v python${{ matrix.system-python }})" "${VIM_VIRTUALENV_PYTHON_BIN}/python" + ln -s "$(command -v python${{ matrix.system-python }})" "${VIM_VIRTUALENV_PYTHON_BIN}/python3" + echo "${VIM_VIRTUALENV_PYTHON_BIN}" >> "${GITHUB_PATH}" + + - name: Set up editor + run: | + dnf install -y vim-enhanced + vim --version | grep -F '+python3/dyn-stable' + + - name: Create virtual environment + run: | + export VIM_VIRTUALENV_TARGET="$(mktemp --directory)" + if [[ "${{ matrix.tool }}" == "venv" ]]; then + python${{ matrix.venv-python }} -m venv --without-pip "${VIM_VIRTUALENV_TARGET}" + else + /usr/bin/python -m virtualenv --no-seed --python ${{ matrix.venv-python }} "${VIM_VIRTUALENV_TARGET}" + fi + echo "VIM_VIRTUALENV_TARGET=${VIM_VIRTUALENV_TARGET}" >> "${GITHUB_ENV}" + + - name: Activate virtual environment + run: | + python --version + if [[ "${{ matrix.kind }}" == "internal" ]]; + then cd "${VIM_VIRTUALENV_TARGET}" + else source "${VIM_VIRTUALENV_TARGET}/bin/activate" + fi + MATRIX_KIND_CODE=$([[ "${{ matrix.kind }}" == "internal" ]] && echo 1 || echo 0) + vim --not-a-term --clean -u "${GITHUB_WORKSPACE}/.github/workflows/minimal.vim" \ + -c "call assert_equal('${{ matrix.tool }}', virtualenv#state('virtualenv_type'))" \ + -c "call assert_equal(${MATRIX_KIND_CODE}, virtualenv#state('virtualenv_internal'))" \ + -c "call assert_equal('${VIM_VIRTUALENV_TARGET}', virtualenv#state('virtualenv_directory'))" \ + -c "call assert_equal('${VIM_VIRTUALENV_TARGET}', py3eval('sys.prefix'))" \ + -c "call assert_equal('${{ matrix.venv-python }}', virtualenv#state('virtualenv_python'))" \ + -c "call assert_equal('${{ matrix.venv-python }}', join(py3eval('sys.version_info[:2]'), '.'))" \ + -c "call g:ExitTest()" diff --git a/autoload/virtualenv.vim b/autoload/virtualenv.vim index c265330..b995a1a 100644 --- a/autoload/virtualenv.vim +++ b/autoload/virtualenv.vim @@ -1,22 +1,8 @@ function! virtualenv#init() let s:state = {} - if has('python3_stable') - let s:vim_major = and(v:python3_version >> 24, 0xff) - let s:vim_minor = and(v:python3_version >> 16, 0xff) - else - let s:vim_major = py3eval('sys.version_info.major') - let s:vim_minor = py3eval('sys.version_info.minor') - endif - let s:custom_project_finder = 'virtualenv#gutentags_project_root_finder' - try - execute 'py3file' fnameescape(g:virtualenv#python_script) - catch - return s:error('failed to load Python virtual environment manager') - endtry - if !empty(g:virtualenv#directory) let g:virtualenv#directory = \ s:normalize_path(fnamemodify(g:virtualenv#directory, ':p')) @@ -125,10 +111,16 @@ function! virtualenv#force_activate(target, ...) let [l:major, l:minor] = l:pyversion call s:error('Python version mismatch') call s:error('Environment version: '.l:major.'.'.l:minor) - call s:error('Vim version: '.s:vim_major.'.'.s:vim_minor) + call s:error('Vim version: '.s:python.major.'.'.s:python.minor) return s:error(l:target.' is not supported') endif + if exists('+pythonthreedll') + let [&pythonthreedll] = s:execute_system_python_command( + \ 'import sysconfig; print(sysconfig.get_config_var("INSTSONAME"))', + \ l:target) + endif + let s:state['virtualenv_type'] = l:env_type let s:state['virtualenv_internal'] = l:internal let s:state['virtualenv_python'] = join(l:pyversion, '.') @@ -282,7 +274,8 @@ function! s:get_env_type(target) return filereadable(s:join_path(a:target, 'uv.lock')) ? 'uv' : '.venv' elseif filereadable(s:join_path(a:target, 'venv/.tox-info.json')) return '.tox' - elseif filereadable(s:join_path(a:target, '.python-version')) && exists('$PYENV_ROOT') + elseif filereadable(s:join_path(a:target, '.python-version')) && + \ exists('$PYENV_ROOT') return 'pyenv' elseif filereadable(s:join_path(a:target, 'bin/activate_this.py')) return filereadable(s:join_path(a:target, 'bin/get_env_details')) @@ -325,11 +318,25 @@ function! s:get_pyversion_external(...) endfunction function! s:is_python_supported(pyversion) + if !exists('s:python') + if has('python3_stable') + let s:python = { + \ 'major': and(v:python3_version >> 24, 0xff), + \ 'minor': and(v:python3_version >> 16, 0xff), + \ } + else + let s:python = { + \ 'major': py3eval('sys.version_info.major'), + \ 'minor': py3eval('sys.version_info.minor'), + \ } + endif + endif + let [l:major, l:minor] = a:pyversion if has('python3_stable') - return (l:major >= s:vim_major) && (l:minor >= s:vim_minor) + return (l:major >= s:python.major) && (l:minor >= s:python.minor) else - return (l:major == s:vim_major) && (l:minor == s:vim_minor) + return (l:major == s:python.major) && (l:minor == s:python.minor) endif endfunction @@ -394,11 +401,22 @@ function! s:normalize_path(path) endfunction " python machinery -function! s:execute_system_python_command(command) - return systemlist("python -B -c 'import sys; ".a:command."'") +function! s:execute_system_python_command(command, root=v:null) + let l:prefix = a:root is v:null ? '' : s:join_path(a:root, 'bin/') + return systemlist(l:prefix."python -B -c 'import sys; ".a:command."'") endfunction function! s:execute_python_command(command, ...) + if !exists('s:python_loaded') + try + execute 'py3file' fnameescape(g:virtualenv#python_script) + catch + throw 'failed to load Python virtual environment manager: '. + \ v:exception + endtry + let s:python_loaded = 1 + endif + let l:command = a:command.((a:0) ? s:build_arguments(a:000) : '') return split(execute('python3 '.l:command), '\n') endfunction diff --git a/plugin/virtualenv.vim b/plugin/virtualenv.vim index 61951c1..b51cf93 100644 --- a/plugin/virtualenv.vim +++ b/plugin/virtualenv.vim @@ -2,7 +2,7 @@ if exists('g:loaded_virtualenv') finish endif -if !has('python3') +if !has('python3_compiled') && !has('python3') echoerr 'vim-virtualenv requires python3 feature to be enabled' finish endif