pyenv-virtualenv for Windows 1.2
A 'pyenv' plugin to manage Python virtual environments, depending on different Python versions, for various Python projects.
Loading...
Searching...
No Matches
pyenv_ptc_3.1.1.bat
Go to the documentation of this file.
1@echo off
2setlocal
3chcp 65001 >nul 2>&1
4
5REM --------------------------------------------------------------------
6REM --- PATCH 1 FOR PYENV-VIRTUALENV 2025-07-14 ---
7REM --------------------------------------------------------------------
8
9REM Determine log level
10if not defined LOG_LEVEL goto log_level1
11 set /a LOG_LEVEL=%LOG_LEVEL%
12 goto log_level2
13:log_level1
14 set /a LOG_LEVEL=20
15:log_level2
16
17REM Determine pyenv root folder
18if not defined PYENV_ROOT goto undefined0
19if not exist "%PYENV_ROOT%" goto nonexist0
20goto continue0
21undefined0:
22 echo ␛[101mCRITICAL Cannot find "PYENV_ROOT" environment variable.␛[0m
23 echo ␛[37mINFO Check/install/configure "pyenv". Then try again.␛[0m
24 exit /b 1
25:nonexist0
26 echo ␛[101mCRITICAL Cannot find environment "pyenv root" directory "%PYENV_ROOT%".␛[0m
27 echo ␛[37mINFO Check/repair/configure "pyenv". Then try again.␛[0m
28 exit /b 1
29:continue0
30
31REM NOTE: In addition all substrings "%~dp0..\" has been replaced
32REM by "%PYENV_ROOT%" to make this script independent from its location
33REM on the system hard disk.
34
35REM --------------------------------------------------------------------
36
37set "pyenv=cscript //nologo "%PYENV_ROOT%libexec\pyenv.vbs""
38
39:: if 'pyenv' called alone, then run pyenv.vbs
40if [%1]==[] (
41 %pyenv% || goto :error
42 exit /b
43)
44
45set "skip=-1"
46for /f "delims=␌" %%i in ('echo skip') do (call :incrementskip)
47if [%skip%]==[0] set "skip_arg="
48if not [%skip%]==[0] set "skip_arg=skip=%skip% "
49
50if /i [%1%2]==[version] call :check_path
51
52:: use pyenv.vbs to aid resolving absolute path of "active" version into 'bindir'
53set "bindir="
54set "extrapaths="
55for /f "%skip_arg%delims=" %%i in ('%pyenv% vname') do call :extrapath "%PYENV_ROOT%versions\%%i"
56
57:: Add %AppData% Python Scripts to %extrapaths%.
58for /F "tokens=1,2 delims=-" %%i in ('%pyenv% vname') do (
59 if /i "%%j" == "win32" (
60 for /F "tokens=1,2,3 delims=." %%a in ("%%i") do (
61 set "extrapaths=%extrapaths%%AppData%\Python\Python%%a%%b-32\Scripts;"
62 )
63 ) else (
64 for /F "tokens=1,2,3 delims=." %%a in ("%%i") do (
65 set "extrapaths=%extrapaths%%AppData%\Python\Python%%a%%b\Scripts;"
66 )
67 )
68)
69
70:: all help implemented as plugin
71if /i [%2]==[--help] goto :plugin
72if /i [%1]==[--help] (
73 call :plugin %2 %1 || goto :error
74 exit /b
75)
76if /i [%1]==[help] (
77 if [%2]==[] call :plugin help --help || goto :error
78 if not [%2]==[] call :plugin %2 --help || goto :error
79 exit /b
80)
81
82:: let pyenv.vbs handle these
83set "commands=rehash global local version vname version-name versions commands shims which whence help --help"
84for %%a in (%commands%) do (
85 if /i [%1]==[%%a] (
86 rem endlocal not really needed here since above commands do not set any variable
87 rem endlocal closed automatically with exit
88 rem no need to update PATH either
89 %pyenv% %* || goto :error
90 exit /b
91 )
92)
93
94:: jump to plugin or fall to exec
95if /i not [%1]==[exec] goto :plugin
96:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
97:exec
98
99if not exist "%bindir%" (
100 echo No global/local python version has been set yet. Please set the global/local version by typing:
101 echo pyenv global 3.7.4
102 echo pyenv local 3.7.4
103 exit /b 1
104)
105
106set cmdline=%*
107set cmdline=%cmdline:~5%
108
109:: update PATH to active version and run command
110:: endlocal needed only if cmdline sets a variable: SET FOO=BAR
111call :remove_shims_from_path
112%cmdline% || goto :error
113
114endlocal
115exit /b
116:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
117:remove_shims_from_path
118set "python_shims=%PYENV_ROOT%shims"
119call :normalizepath "%python_shims%" python_shims
120set "_path=%path%"
121set "path=%extrapaths%"
122
123:: arcane magic courtesy of StackOverflow question 5471556
124:: https://stackoverflow.com/a/7940444/381865
125setlocal DisableDelayedExpansion
126:: escape all special characters
127set "_path=%_path:"=""%"
128set "_path=%_path:^=^^%"
129set "_path=%_path:&=^&%"
130set "_path=%_path:|=^|%"
131set "_path=%_path:<=^<%"
132set "_path=%_path:>=^>%"
133set "_path=%_path:;=^;^;%"
134:: the 'missing' quotes below are intended
135set _path=%_path:""="%
136:: " => ""Q (like quote)
137set "_path=%_path:"=""Q%"
138:: ;; => "S"S (like semicolon)
139set "_path=%_path:;;="S"S%"
140set "_path=%_path:^;^;=;%"
141set "_path=%_path:""="%"
142setlocal EnableDelayedExpansion
143
144:: "Q => <empty>
145set "_path=!_path:"Q=!"
146:: "S"S => ";"
147for %%a in ("!_path:"S"S=";"!") do (
148 if "!!"=="" (
149 endlocal
150 endlocal
151 )
152 if %%a neq "" (
153 if /i not "%%~dpfa"=="%python_shims%" call :append_to_path %%~dpfa
154 )
155)
156
157exit /b
158:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
159:append_to_path
160set "path=%path%%*;"
161exit /b
162:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
163:plugin
164set "exe=%PYENV_ROOT%libexec\pyenv-%1"
165rem TODO needed?
166call :normalizepath %exe% exe
167
168if exist "%exe%.bat" (
169 set "exe=call "%exe%.bat""
170
171) else if exist "%exe%.cmd" (
172 set "exe=call "%exe%.cmd""
173
174) else if exist "%exe%.vbs" (
175 set "exe=cscript //nologo "%exe%.vbs""
176
177) else if exist "%exe%.lnk" (
178 set "exe=start '' "%exe%.bat""
179) else (
180 REM --------------------------------------------------------------------
181 REM --- PATCH 2 FOR PYENV-VIRTUALENV 2025-07-14 ---
182 REM --------------------------------------------------------------------
183 if %LOG_LEVEL% leq 15 echo ␛[94mVERBOSE Cannot find executable "%exe%.*".␛[0m
184 if %LOG_LEVEL% leq 15 echo ␛[94mVERBOSE Redirecting command "pyenv %1" to related plugin ...␛[0m
185 if %LOG_LEVEL% leq 15 echo ␛[94mVERBOSE Working around entropy caused by deviations from 'pyenv-virtualenv' common command design ...␛[0m
186 REM Determine the plugin name
187 REM NOTE: The "virtualenv" command will be redirected to 'pyenv-virtualenv'.
188 if "%1"=="virtualenv" goto redirect_to_virtualenv
189 REM NOTE: The "virtualenvs" command will be redirected to 'pyenv-virtualenv'.
190 if "%1"=="virtualenvs" goto redirect_to_virtualenv
191 REM NOTE: The "activate" command will be redirected to 'pyenv-virtualenv'.
192 if "%1"=="activate" goto redirect_to_virtualenv
193 REM NOTE: The "deactivate" command will be redirected to 'pyenv-virtualenv'.
194 if "%1"=="deactivate" goto redirect_to_virtualenv
195 goto endworkaround1
196 :redirect_to_virtualenv
197 set "PLUGIN_NAME=virtualenv"
198 goto continue1
199 :endworkaround1
200 if %LOG_LEVEL% leq 15 echo ␛[94mVERBOSE Detecting plugin name in command name "%1" ...␛[0m
201 for /f "tokens=1 delims=-" %%a in ("%1") do set "PLUGIN_NAME=%%a"
202 REM NOTE: All commands starting with "venv-" will be redirected to "pyenv-virtualenv".
203 if "%PLUGIN_NAME%"=="venv" goto redirect_to_virtualenv
204 REM NOTE: All other commands will be forwarded to other installed plugins if available.
205 goto continue1
206 :continue1
207 if %LOG_LEVEL% leq 15 echo ␛[94mVERBOSE Plugin name: "%PLUGIN_NAME%".␛[0m
208 if %LOG_LEVEL% leq 15 echo ␛[94mVERBOSE Plugin command: "%1".␛[0m
209 REM Forward the command to the detected plugin
210 set "exe=%PYENV_ROOT%plugins\pyenv-%PLUGIN_NAME%\libexec\pyenv-%1"
211 call :normalizepath %exe% exe
212 if %LOG_LEVEL% leq 15 echo ␛[94mVERBOSE Plugin call: "%exe%".␛[0m
213 REM Calculate path to existing executable only
214 if exist "%exe%.bat" (
215 set "exe=call "%exe%.bat""
216 ) else if exist "%exe%.cmd" (
217 set "exe=call "%exe%.cmd""
218 ) else if exist "%exe%.vbs" (
219 set "exe=cscript //nologo "%exe%.vbs""
220 ) else if exist "%exe%.lnk" (
221 set "exe=start '' "%exe%.bat""
222 ) else (
223 REM Not existing
224 if %LOG_LEVEL% leq 15 echo ␛[94mVERBOSE Cannot find executable "%exe%.*".␛[0m
225 echo pyenv: no such command '%1'
226 REM Cancel with error level 1
227 exit /b 1
228 )
229 REM The following 2 lines are obsolete now and has been commented:
230 REM echo pyenv: no such command '%1'
231 REM exit /b 1
232
233 REM ------------------------------------------------------------------
234)
235
236:: replace first arg with %exe%
237set cmdline=%*
238set cmdline=%cmdline:^=^^%
239set cmdline=%cmdline:!=^!%
240set "arg1=%1"
241set "len=1"
242:loop_len
243set /a len=%len%+1
244set "arg1=%arg1:~1%"
245if not [%arg1%]==[] goto :loop_len
246
247setlocal enabledelayedexpansion
248set cmdline=!exe! !cmdline:~%len%!
249:: run command (no need to update PATH for plugins)
250:: endlocal needed to ensure exit will not automatically close setlocal
251:: otherwise PYTHON_VERSION will be lost
252endlocal && endlocal && %cmdline% || goto :error
253exit /b
254:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
255:: convert path which may have relative nodes (.. or .)
256:: to its absolute value so can be used in PATH
257:normalizepath
258set "%~2=%~dpf1"
259goto :eof
260:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
261:: compute list of paths to add for all activated python versions
262:extrapath
263call :normalizepath %1 bindir
264set "extrapaths=%extrapaths%%bindir%;%bindir%\Scripts;%bindir%\bin;"
265goto :eof
266:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
267:: check pyenv python shim is first in PATH
268:check_path
269set "python_shim=%PYENV_ROOT%shims\python.bat"
270if not exist "%python_shim%" goto :eof
271call :normalizepath "%python_shim%" python_shim
272set "python_where="
273for /f "%skip_arg%delims=" %%a in ('where python') do (
274 if /i "%python_shim%"=="%%~dpfa" goto :eof
275 call :set_python_where %%~dpfa
276)
277call :bad_path "%python_where%"
278exit /b
279:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
280:: set python_where variable if empty
281:set_python_where
282if "%python_where%"=="" set "python_where=%*"
283goto :eof
284:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
285:: tell bad PATH and exit
286:bad_path
287set "bad_python=%~1"
288set "bad_dir=%~dp1"
289echo ␛[91mFATAL: Found ␛[95m%bad_python%␛[91m version before pyenv in PATH.␛[0m
290echo ␛[91mPlease remove ␛[95m%bad_dir%␛[91m from PATH for pyenv to work properly.␛[0m
291goto :eof
292:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
293:: if AutoExec/AutoRun is configured for cmd it probably ends with the `cls` command
294:: meaning there will be a Form Feed (U+000C) included in the output.
295:: so we add it as a dilimiter so that we can skip x number of lines.
296:: we find out how many to skip and pass that tot the skip option of the for loop,
297:: EXCEPT skip=0 gives errors...
298:: so we prepend every command with `echo skip` to force skip being at least 1
299:incrementskip
300set /a skip=%skip%+1
301goto :eof
302
303:error
304exit /b %errorlevel%
305