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-virtualenv Namespace Reference

Functions

int run (argparse.Namespace args)
 Sub routine to run the application.
 
tuple[(argparse.Namespace, None), int] parseCliArguments1 ()
 Parse CLI arguments for this application.
 
tuple[(argparse.Namespace, None), int] parseCliArguments2 ()
 Parse CLI arguments for this application.
 
int displayDocumentation ()
 Display the version number and generating procedure for "pyenv-virtualenv" for Windows.
 
int displayVersionNumber ()
 Display the version number and generating procedure for "pyenv-virtualenv" for Windows.
 
int main ()
 Main routine of the application.
 

Function Documentation

◆ run()

int pyenv-virtualenv.run ( argparse.Namespace args)

Sub routine to run the application.

Parameters
argsParsed command line arguments of this application.
Returns
RC = 0 or other values in case of error.

Definition at line 53 of file pyenv-virtualenv.py.

53def run(args: argparse.Namespace) -> int:
54 cwd = os.getcwd()
55 # noinspection PyBroadException
56 try:
57 while True:
58 # Force to select the "Version" argument,
59 # even if it is not given.
60 try:
61 # Use the given "version" argument
62 version: str = args.version
63 realm = 'argument'
64 except AttributeError:
65 # Use the "pyenv" python version
66 version, realm = hlp.getPythonVersion()
67 if version == '':
68 log.error('Cannot determine the version number of the "pyenv" Python version.')
69 log.info('Trying next, set the version number explicitly. See --help.')
70 rc = 1
71 break
72 # Optimize arguments
73 version = version.strip()
74 name: str = hlp.fName(args.name)
75 props: bool = args.props
76 # Output actual arguments
77 log.info('Creating Python virtual environment in "pyenv":')
78 log.info(f' * Version: {version} ({realm})')
79 log.info(f' * Name: {name}')
80 log.info(f' * Set project properties: {props}')
81 # Filter/select best virtual environment capable version
82 selected_version_dir, rc = hlp.selectVersionDir(
83 version,
84 realm,
85 venv_capable=True
86 )
87 if rc != 0:
88 break
89 selected_version = os.path.basename(selected_version_dir)
90 # Check and make directories
91 envs_dir = os.path.join(selected_version_dir, 'envs')
92 if not os.path.isdir(envs_dir):
93 log.verbose(f'Creating "{envs_dir}" folder ...')
94 os.mkdir(envs_dir)
95 # Create Python virtual environment
96 venv_dir = os.path.join(envs_dir, name)
97 if os.path.isdir(venv_dir):
98 # Remove existing directory
99 log.verbose(f'Overriding folder "{venv_dir}" ...')
100 shutil.rmtree(venv_dir)
101 else:
102 log.verbose(f'Creating folder "{venv_dir}" ...')
103 # Get executable path for the selected Python version
104 python_exe_path = os.path.join(
105 selected_version_dir,
106 'python.exe'
107 )
108 # Generate Python virtual environment
109 cmd = [
110 python_exe_path,
111 '-m',
112 'venv',
113 venv_dir
114 ]
115 log.verbose(f'Execute: {python_exe_path}')
116 log.info('This will take some seconds ...')
117 # noinspection SpellCheckingInspection
118 cp = subprocess.run(
119 cmd,
120 stdout=subprocess.DEVNULL,
121 stderr=subprocess.DEVNULL,
122 shell=True
123 )
124 rc = cp.returncode
125 if rc != 0:
126 log.error(
127 'Unexpectedly cannot create ' +
128 'Python virtual environment.'
129 )
130 log.info('Check/repair Python and "pyenv". Then try again.')
131 rc = 1
132 break
133 # Create junction to virtual environment directory
134 # within the Python versions directory.
135 venv_version_dir = os.path.join(
136 os.environ['PYENV_ROOT'],
137 'versions',
138 f'{name}-{selected_version}'
139 )
140 if hlp.isJunction(venv_version_dir):
141 # Remove existing junction
143 'Overriding junction "{}" ...'.format(
144 venv_version_dir
145 )
146 )
147 os.remove(venv_version_dir)
148 else:
150 'Creating junction "{}" ...'.format(
151 venv_version_dir
152 )
153 )
154 # noinspection SpellCheckingInspection
155 cmd = [
156 'mklink',
157 '/J',
158 venv_version_dir,
159 venv_dir
160 ]
161 log.verbose(f'Execute: {cmd}')
162 cp = subprocess.run(
163 cmd,
164 stdout=subprocess.DEVNULL,
165 stderr=subprocess.DEVNULL,
166 shell=True
167 )
168 rc = cp.returncode
169 if rc != 0:
170 log.error(
171 'Unexpectedly cannot create junction to ' +
172 'Python virtual environment directory.'
173 )
174 log.info(
175 'Check/repair security properties ' +
176 'of the junction. Then try again.'
177 )
178 rc = 1
179 break
180 if props:
181 log.verbose('Setting project properties ...')
182 rc = hlp.setProjectProperties(selected_version, name)
183 if rc == 0:
184 log.verbose('Project properties successfully set.')
185 else:
186 log.error('Cannot set project properties.')
187 # Log success message
189 f'Virtual environment "{name}" is installed ' +
190 f'in "pyenv", depending on "Python {selected_version}".'
191 )
192 # Go on
193 break
194 # End while
195 except:
196 log.error(sys.exc_info())
197 rc = 1
198 finally:
199 os.chdir(cwd)
200 return rc
201
202
203# --- MAIN --------------------------------------------------------------
204
tuple[(str, None), int] selectVersionDir(str ver, str realm, venv_capable=False)
Find the best version, matching the requirements.
Definition hlp.py:435
str fName(str natural_name)
Convert a natural name into stripped functional name, without spaces, which is lowercase,...
Definition hlp.py:71
(str, str) getPythonVersion()
Get selected global/local Python version in "pyenv".
Definition hlp.py:331
bool isJunction(str path)
Check if path is a junction, which has been created e.g.
Definition hlp.py:550
int setProjectProperties(str ver, str env)
Set/override project property files.
Definition hlp.py:611
verbose((str, tuple) msg)
Log verbose message colored to console only.
Definition log.py:209
success((str, tuple) msg)
Log success message colored to console only.
Definition log.py:185
info((str, tuple) msg)
Log info message colored to console only.
Definition log.py:203
error((str, tuple) msg)
Log error message colored to console only.
Definition log.py:179

◆ parseCliArguments1()

tuple[(argparse.Namespace, None), int] pyenv-virtualenv.parseCliArguments1 ( )

Parse CLI arguments for this application.



Implement this as required, but don't touch the interface definition for input and output.

Returns
A tuple of:
  • Namespace to read arguments in "dot" notation or None in case of help or error.
  • RC = 0 or another value in case of error.

Definition at line 214 of file pyenv-virtualenv.py.

214def parseCliArguments1() -> tuple[(argparse.Namespace, None), int]:
215 rc: int = 0
216 # noinspection PyBroadException
217 try:
218 parser = argparse.ArgumentParser(
219# --- BEGIN CHANGE -----------------------------------------------------
220 prog='pyenv virtualenv',
221 description='Create a version-assigned and named ' +
222 'Python virtual environment in "pyenv".'
223 )
224 # Add flag
225 parser.add_argument(
226 '-p', '--props',
227 dest='props',
228 action=argparse.BooleanOptionalAction,
229 help='As project properties, add the files ' +
230 '`.python-version` and `.python-env` to CWD. Default: --no_props.'
231 )
232 # Add positional str argument
233 parser.add_argument(
234 'name',
235 help='Short name of the new Python virtual environment.'
236 )
237# --- END CHANGE -------------------------------------------------------
238 return parser.parse_args(), rc
239 except SystemExit:
240 return None, 0
241 except:
242 log.error(sys.exc_info())
243 return None, 1
244

◆ parseCliArguments2()

tuple[(argparse.Namespace, None), int] pyenv-virtualenv.parseCliArguments2 ( )

Parse CLI arguments for this application.



Implement this as required, but don't touch the interface definition for input and output.

Returns
A tuple of:
  • Namespace to read arguments in "dot" notation or None in case of help or error.
  • RC = 0 or another value in case of error.

Definition at line 254 of file pyenv-virtualenv.py.

254def parseCliArguments2() -> tuple[(argparse.Namespace, None), int]:
255 rc: int = 0
256 # noinspection PyBroadException
257 try:
258 parser = argparse.ArgumentParser(
259# --- BEGIN CHANGE -----------------------------------------------------
260 prog='pyenv virtualenv',
261 description='Create a version-assigned and named ' +
262 'Python virtual environment in "pyenv".'
263 )
264 # Add flag
265 parser.add_argument(
266 '-p', '--props',
267 dest='props',
268 action=argparse.BooleanOptionalAction,
269 help='As project properties, add the files ' +
270 '`.python-version` and `.python-env` to CWD. Default: --no_props.'
271 )
272 # Add positional str argument
273 parser.add_argument(
274 'version',
275 help='Python version, which must be already installed ' +
276 'in "pyenv".'
277 )
278 # Add positional str argument
279 parser.add_argument(
280 'name',
281 help='Short name of the new Python virtual environment.'
282 )
283# --- END CHANGE -------------------------------------------------------
284 return parser.parse_args(), rc
285 except SystemExit:
286 return None, 0
287 except:
288 log.error(sys.exc_info())
289 return None, 1
290

◆ displayDocumentation()

int pyenv-virtualenv.displayDocumentation ( )

Display the version number and generating procedure for "pyenv-virtualenv" for Windows.

Returns
RC = 0 or another value in case of error

Definition at line 294 of file pyenv-virtualenv.py.

294def displayDocumentation() -> int:
295 # noinspection PyBroadException
296 try:
297 cmd = os.path.join(
298 os.environ['PYENV_ROOT'],
299 'plugins',
300 'pyenv-virtualenv',
301 'docs',
302 'open_doxygen_docs.bat'
303 )
304 log.verbose(f'Execute: {cmd}')
305 cp = subprocess.run(
306 cmd,
307 shell=True
308 )
309 rc = cp.returncode
310 if rc != 0:
311 log.error(f'Displaying Doxygen documentation failed (RC = {rc}.')
312 log.info('Check/reinstall "pyenv-virtualenv". Then try again. ')
313 log.notice('CANCELED.')
314 rc = 130
315 except:
316 log.error(sys.exc_info())
317 rc = 1
318 return rc
319
notice((str, tuple) msg)
Log notice message colored to console only.
Definition log.py:197

◆ displayVersionNumber()

int pyenv-virtualenv.displayVersionNumber ( )

Display the version number and generating procedure for "pyenv-virtualenv" for Windows.

Returns
RC = 0 or another value in case of error

Definition at line 323 of file pyenv-virtualenv.py.

323def displayVersionNumber() -> int:
324 # noinspection PyBroadException
325 try:
326 file_path = os.path.join(
327 os.environ['PYENV_ROOT'],
328 'plugins',
329 'pyenv-virtualenv',
330 '.version'
331 )
332 if not os.path.isfile(file_path):
333 log.error('Cannot display version number.')
334 log.info(f'Reinstall the missing file "{file_path}".')
335 log.notice('CANCELED.')
336 return 130
337 with open(file_path, 'r') as f:
338 version = f.read().strip()
339 # noinspection SpellCheckingInspection
340 print(f'"\x1b[92mpyenv-virtualenv\x1b[0m" for Windows \x1b[96m{version}\x1b[0m (\x1b[93mpython -m venv\x1b[0m).')
341 return 0
342 except:
343 log.error(sys.exc_info())
344 return 1
345

◆ main()

int pyenv-virtualenv.main ( )

Main routine of the application.

Returns
RC = 0 or other values in case of error.

Definition at line 349 of file pyenv-virtualenv.py.

349def main() -> int:
350 # noinspection PyBroadException
351 try:
352 while True:
353 # Audit the operating system platform
354 rc = hlp.auditPlatform('Windows')
355 if rc != 0:
356 # Deviation: Reject unsupported platform
357 break
358 # Audit the global Python version number
360 if rc != 0:
361 # Deviation: Reject unsupported Python version
362 break
363 # Initialize the colored logging to console
365 # Audit the "pyenv" version number
366 rc = hlp.auditPyEnv('3')
367 if rc != 0:
368 # Deviation: Reject unsupported "pyenv" version
369 break
370 # Parse arguments
371 # NOTE: Sorry, this is a complicated workaround to
372 # bypass the lack of applicability of Python "ArgumentParser"
373 # class. This simple use case to set required = False for
374 # positional argument is not included and must be patched
375 # away here.
376 log.verbose('Parsing arguments ...')
377 args = None
378 args_list = sys.argv.copy() # Clone
379 # Strip program executable path
380 args_list.pop(0)
381 # Detect and remove all optional arguments,
382 # which are not related to the positional arguments
383 help_requested = False
384 version_requested = False
385 docs_requested = False
386 for i in reversed(range(len(args_list))):
387 arg = args_list[i]
388 if arg in ['-h', '--help']:
389 help_requested = True
390 args_list.pop(i)
391 elif arg in ['-v', '--version']:
392 version_requested = True
393 args_list.pop(i)
394 elif arg in ['-d', '--docs']:
395 docs_requested = True
396 args_list.pop(i)
397 # Calculate the count of positional arguments
398 positional_count = len(args_list)
399 if docs_requested:
400 rc = displayDocumentation()
401 break
402 elif version_requested:
403 rc = displayVersionNumber()
404 break
405 elif (
406 help_requested
407 or
408 (positional_count not in [1, 2])
409 ):
410 text = ("""
411Usage: pyenv virtualenv [-h] [-v] [-p | --props | --no-props ] [version] [name]
412
413Create a version-assigned and named Python virtual environment in "pyenv".
414
415Positional arguments (which can be omitted):
416 [version] Python version, which must be already installed in
417 "pyenv". Default: The global Python version.
418 [name] Short name of the new Python virtual environment.
419
420Options:
421 -h, --help Show this help message and exit.
422 -p, --props, --no-props
423 Add the files `.python-version` and `.python-env`
424 as project properties to CWD. Default: --no_props.
425 -v, --version Display the version number of this "pyenv-virtualenv"
426 release and ignore all other arguments.
427 """).strip()
428 print(text)
429 elif positional_count == 1:
430 # Case 1: single positional argument must be "Name"
431 args, rc = parseCliArguments1()
432 elif positional_count == 2:
433 # Case 2: duo positional arguments must be "Version" and "Name"
434 args, rc = parseCliArguments2()
435 if rc != 0:
436 break
437 if args is None: # -h, --help
438 break
439 # Run this application
440 log.verbose('Running application ...')
441 rc = run(args)
442 if rc != 0:
443 break
444 # Go on
445 break
446 # End while
447 except Exception as exc:
449 log.error(sys.exc_info())
450 else:
451 print(
452 '\x1b[91mERROR: Unexpected error "%s".\x1b[0m'
453 %
454 str(exc)
455 )
456 rc = 1
457 return rc
458
459
int auditPyEnv(str min_ver)
Check if "pyenv" version is greater or equal the given minimal version.
Definition hlp.py:213
int auditGlobalPythonVersion(str min_ver)
Check if Python version is greater or equal the given minimal version.
Definition hlp.py:148
int auditPlatform(str name)
Check if the program in running on the required platform.
Definition hlp.py:85
initLogging()
Initialize the logging.
Definition log.py:71
bool isInitialized()
Definition log.py:90