@@ -310,6 +310,226 @@ def test_sysconfig(self):
310310 out , err = check_output (cmd , encoding = 'utf-8' )
311311 self .assertEqual (out .strip (), expected , err )
312312
313+ @requireVenvCreate
314+ def test_version_mismatch_warning (self ):
315+ """
316+ Test that a warning is emitted when running a venv created for a
317+ different minor Python version.
318+ """
319+ rmtree (self .env_dir )
320+
321+ wrong_minor = sys .version_info .minor + 1
322+ self .run_with_capture (venv .create , self .env_dir , with_pip = False )
323+
324+ cfg_path = self .get_env_file ('pyvenv.cfg' )
325+ with open (cfg_path , 'r' , encoding = 'utf-8' ) as f :
326+ cfg_content = f .read ()
327+
328+ new_version = f"{ sys .version_info .major } .{ wrong_minor } "
329+ if 'version =' in cfg_content :
330+ cfg_content = re .sub (r'version = \d+\.\d+' , f'version = { new_version } ' , cfg_content )
331+
332+ cfg_content += f'\n version_info = { new_version } \n '
333+
334+ with open (cfg_path , 'w' , encoding = 'utf-8' ) as f :
335+ f .write (cfg_content )
336+
337+ envpy = self .envpy (real_env_dir = True )
338+
339+ proc = subprocess .run (
340+ [envpy , '-c' , 'import sys; print("done")' ],
341+ capture_output = True ,
342+ text = True ,
343+ env = {** os .environ , "PYTHONHOME" : "" }
344+ )
345+
346+ self .assertIn (f"Python { sys .version_info .major } .{ wrong_minor } " , proc .stderr )
347+ self .assertIn ("Consider running `python -m venv --upgrade`" , proc .stderr )
348+
349+ @requireVenvCreate
350+ def test_version_info_mismatch_warning (self ):
351+ """
352+ Test that a warning is emitted when version_info (used by virtualenv)
353+ indicates a different minor version.
354+ """
355+ rmtree (self .env_dir )
356+ wrong_minor = sys .version_info .minor + 1
357+ self .run_with_capture (venv .create , self .env_dir , with_pip = False )
358+
359+ cfg_path = self .get_env_file ('pyvenv.cfg' )
360+ with open (cfg_path , 'r' , encoding = 'utf-8' ) as f :
361+ cfg_content = f .read ()
362+
363+ # Add only version_info, don't modify version
364+ new_version = f"{ sys .version_info .major } .{ wrong_minor } "
365+ cfg_content += f'\n version_info = { new_version } \n '
366+
367+ with open (cfg_path , 'w' , encoding = 'utf-8' ) as f :
368+ f .write (cfg_content )
369+
370+ envpy = self .envpy (real_env_dir = True )
371+ proc = subprocess .run (
372+ [envpy , '-c' , 'import sys; print("done")' ],
373+ capture_output = True ,
374+ text = True ,
375+ env = {** os .environ , "PYTHONHOME" : "" }
376+ )
377+
378+ self .assertIn (f"Python { sys .version_info .major } .{ wrong_minor } " , proc .stderr )
379+ self .assertIn ("Consider running `python -m venv --upgrade`" , proc .stderr )
380+
381+ @requireVenvCreate
382+ def test_version_match_no_warning (self ):
383+ """
384+ Test that no warning is emitted when the venv version matches.
385+ """
386+ rmtree (self .env_dir )
387+
388+ self .run_with_capture (venv .create , self .env_dir , with_pip = False )
389+ cfg_path = self .get_env_file ('pyvenv.cfg' )
390+ with open (cfg_path , 'r' , encoding = 'utf-8' ) as f :
391+ cfg_content = f .read ()
392+ expected_version = f"{ sys .version_info .major } .{ sys .version_info .minor } "
393+
394+ with open (cfg_path , 'w' , encoding = 'utf-8' ) as f :
395+ f .write (cfg_content )
396+ envpy = self .envpy (real_env_dir = True )
397+ proc = subprocess .run (
398+ [envpy , '-c' , 'import sys; print("done")' ],
399+ capture_output = True ,
400+ text = True ,
401+ env = {** os .environ , "PYTHONHOME" : "" }
402+ )
403+
404+ self .assertNotIn ("Consider running `python -m venv --upgrade`" , proc .stderr )
405+
406+ @requireVenvCreate
407+ def test_malformed_version_warning (self ):
408+ """
409+ Test that a warning is emitted on malformed version string
410+ in pyenv.cfg
411+ """
412+ rmtree (self .env_dir )
413+
414+ self .run_with_capture (venv .create , self .env_dir , with_pip = False )
415+
416+ cfg_path = self .get_env_file ('pyvenv.cfg' )
417+ with open (cfg_path , 'r' , encoding = 'utf-8' ) as f :
418+ cfg_content = f .read ()
419+
420+ malformed_version = "not.a.version"
421+ if 'version =' in cfg_content :
422+ cfg_content = re .sub (r'version = .+' , f'version = { malformed_version } ' , cfg_content )
423+
424+ with open (cfg_path , 'w' , encoding = 'utf-8' ) as f :
425+ f .write (cfg_content )
426+
427+ envpy = self .envpy (real_env_dir = True )
428+ proc = subprocess .run (
429+ [envpy , '-c' , 'import sys; print("done")' ],
430+ capture_output = True ,
431+ text = True ,
432+ env = {** os .environ , "PYTHONHOME" : "" }
433+ )
434+ self .assertIn ("Malformed version string" , proc .stderr )
435+ self .assertIn (malformed_version , proc .stderr )
436+
437+ @requireVenvCreate
438+ def test_malformed_version_info_warning (self ):
439+ """
440+ Test that a warning is emitted on malformed version_info string
441+ in pyenv.cfg
442+ """
443+ rmtree (self .env_dir )
444+ self .run_with_capture (venv .create , self .env_dir , with_pip = False )
445+
446+ cfg_path = self .get_env_file ('pyvenv.cfg' )
447+ with open (cfg_path , 'r' , encoding = 'utf-8' ) as f :
448+ cfg_content = f .read ()
449+
450+ malformed_version = "invalid.version"
451+ cfg_content += f'\n version_info = { malformed_version } \n '
452+
453+ with open (cfg_path , 'w' , encoding = 'utf-8' ) as f :
454+ f .write (cfg_content )
455+
456+ envpy = self .envpy (real_env_dir = True )
457+ proc = subprocess .run (
458+ [envpy , '-c' , 'import sys; print("done")' ],
459+ capture_output = True ,
460+ text = True ,
461+ env = {** os .environ , "PYTHONHOME" : "" }
462+ )
463+
464+ self .assertIn ("Malformed version_info string" , proc .stderr )
465+ self .assertIn (malformed_version , proc .stderr )
466+
467+ @requireVenvCreate
468+ def test_conflicting_version_fields (self ):
469+ """
470+ Test behavior when both version and version_info are present
471+ but contain different values. Should warn based on first mismatch found.
472+ """
473+ rmtree (self .env_dir )
474+ wrong_minor = sys .version_info .minor + 1
475+ self .run_with_capture (venv .create , self .env_dir , with_pip = False )
476+
477+ cfg_path = self .get_env_file ('pyvenv.cfg' )
478+ with open (cfg_path , 'r' , encoding = 'utf-8' ) as f :
479+ cfg_content = f .read ()
480+
481+ version_wrong = f"{ sys .version_info .major } .{ wrong_minor } "
482+ if 'version =' in cfg_content :
483+ cfg_content = re .sub (r'version = \d+\.\d+' , f'version = { version_wrong } ' , cfg_content )
484+
485+ version_info_wrong = f"{ sys .version_info .major } .{ wrong_minor + 1 } "
486+ cfg_content += f'\n version_info = { version_info_wrong } \n '
487+
488+ with open (cfg_path , 'w' , encoding = 'utf-8' ) as f :
489+ f .write (cfg_content )
490+
491+ envpy = self .envpy (real_env_dir = True )
492+ proc = subprocess .run (
493+ [envpy , '-c' , 'import sys; print("done")' ],
494+ capture_output = True ,
495+ text = True ,
496+ env = {** os .environ , "PYTHONHOME" : "" }
497+ )
498+
499+ self .assertIn ("Consider running `python -m venv --upgrade`" , proc .stderr )
500+ self .assertEqual (proc .stderr .count ("Consider running `python -m venv --upgrade`" ), 1 )
501+
502+ @requireVenvCreate
503+ def test_different_major_version_no_warning (self ):
504+ """
505+ Test that no warning is emitted when major version differs.
506+ The warning should only trigger for same major, different minor.
507+ """
508+ rmtree (self .env_dir )
509+ self .run_with_capture (venv .create , self .env_dir , with_pip = False )
510+
511+ cfg_path = self .get_env_file ('pyvenv.cfg' )
512+ with open (cfg_path , 'r' , encoding = 'utf-8' ) as f :
513+ cfg_content = f .read ()
514+
515+ different_major = sys .version_info .major + 1
516+ new_version = f"{ different_major } .{ sys .version_info .minor } "
517+
518+ if 'version =' in cfg_content :
519+ cfg_content = re .sub (r'version = \d+\.\d+' , f'version = { new_version } ' , cfg_content )
520+ with open (cfg_path , 'w' , encoding = 'utf-8' ) as f :
521+ f .write (cfg_content )
522+
523+ envpy = self .envpy (real_env_dir = True )
524+ proc = subprocess .run (
525+ [envpy , '-c' , 'import sys; print("done")' ],
526+ capture_output = True ,
527+ text = True ,
528+ env = {** os .environ , "PYTHONHOME" : "" }
529+ )
530+
531+ self .assertNotIn ("Consider running `python -m venv --upgrade`" , proc .stderr )
532+
313533 @requireVenvCreate
314534 @unittest .skipUnless (can_symlink (), 'Needs symlinks' )
315535 def test_sysconfig_symlinks (self ):
0 commit comments