| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  | import contextlib | 
					
						
							|  |  |  | import itertools | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | import re | 
					
						
							| 
									
										
										
										
											2022-04-07 00:09:54 +01:00
										 |  |  | import shutil | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  | import subprocess | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | import sysconfig | 
					
						
							|  |  |  | import tempfile | 
					
						
							|  |  |  | import unittest | 
					
						
							|  |  |  | from pathlib import Path | 
					
						
							|  |  |  | from test import support | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if sys.platform != "win32": | 
					
						
							|  |  |  |     raise unittest.SkipTest("test only applies to Windows") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Get winreg after the platform check | 
					
						
							|  |  |  | import winreg | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PY_EXE = "py.exe" | 
					
						
							|  |  |  | if sys.executable.casefold().endswith("_d.exe".casefold()): | 
					
						
							|  |  |  |     PY_EXE = "py_d.exe" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Registry data to create. On removal, everything beneath top-level names will | 
					
						
							|  |  |  | # be deleted. | 
					
						
							|  |  |  | TEST_DATA = { | 
					
						
							|  |  |  |     "PythonTestSuite": { | 
					
						
							|  |  |  |         "DisplayName": "Python Test Suite", | 
					
						
							|  |  |  |         "SupportUrl": "https://www.python.org/", | 
					
						
							|  |  |  |         "3.100": { | 
					
						
							|  |  |  |             "DisplayName": "X.Y version", | 
					
						
							|  |  |  |             "InstallPath": { | 
					
						
							|  |  |  |                 None: sys.prefix, | 
					
						
							|  |  |  |                 "ExecutablePath": "X.Y.exe", | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         "3.100-32": { | 
					
						
							|  |  |  |             "DisplayName": "X.Y-32 version", | 
					
						
							|  |  |  |             "InstallPath": { | 
					
						
							|  |  |  |                 None: sys.prefix, | 
					
						
							|  |  |  |                 "ExecutablePath": "X.Y-32.exe", | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         "3.100-arm64": { | 
					
						
							|  |  |  |             "DisplayName": "X.Y-arm64 version", | 
					
						
							|  |  |  |             "InstallPath": { | 
					
						
							|  |  |  |                 None: sys.prefix, | 
					
						
							|  |  |  |                 "ExecutablePath": "X.Y-arm64.exe", | 
					
						
							|  |  |  |                 "ExecutableArguments": "-X fake_arg_for_test", | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         "ignored": { | 
					
						
							|  |  |  |             "DisplayName": "Ignored because no ExecutablePath", | 
					
						
							|  |  |  |             "InstallPath": { | 
					
						
							|  |  |  |                 None: sys.prefix, | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2023-02-01 21:06:56 +00:00
										 |  |  |     }, | 
					
						
							|  |  |  |     "PythonTestSuite1": { | 
					
						
							|  |  |  |         "DisplayName": "Python Test Suite Single", | 
					
						
							|  |  |  |         "3.100": { | 
					
						
							|  |  |  |             "DisplayName": "Single Interpreter", | 
					
						
							|  |  |  |             "InstallPath": { | 
					
						
							|  |  |  |                 None: sys.prefix, | 
					
						
							|  |  |  |                 "ExecutablePath": sys.executable, | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }, | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-07 00:09:54 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | TEST_PY_ENV = dict( | 
					
						
							|  |  |  |     PY_PYTHON="PythonTestSuite/3.100", | 
					
						
							|  |  |  |     PY_PYTHON2="PythonTestSuite/3.100-32", | 
					
						
							|  |  |  |     PY_PYTHON3="PythonTestSuite/3.100-arm64", | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  | TEST_PY_DEFAULTS = "\n".join([ | 
					
						
							| 
									
										
										
										
											2022-04-07 00:09:54 +01:00
										 |  |  |     "[defaults]", | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |     *[f"{k[3:].lower()}={v}" for k, v in TEST_PY_ENV.items()], | 
					
						
							| 
									
										
										
										
											2022-04-07 00:09:54 +01:00
										 |  |  | ]) | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  | TEST_PY_COMMANDS = "\n".join([ | 
					
						
							|  |  |  |     "[commands]", | 
					
						
							|  |  |  |     "test-command=TEST_EXE.exe", | 
					
						
							|  |  |  | ]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  | def create_registry_data(root, data): | 
					
						
							|  |  |  |     def _create_registry_data(root, key, value): | 
					
						
							|  |  |  |         if isinstance(value, dict): | 
					
						
							|  |  |  |             # For a dict, we recursively create keys | 
					
						
							|  |  |  |             with winreg.CreateKeyEx(root, key) as hkey: | 
					
						
							|  |  |  |                 for k, v in value.items(): | 
					
						
							|  |  |  |                     _create_registry_data(hkey, k, v) | 
					
						
							|  |  |  |         elif isinstance(value, str): | 
					
						
							|  |  |  |             # For strings, we set values. 'key' may be None in this case | 
					
						
							|  |  |  |             winreg.SetValueEx(root, key, None, winreg.REG_SZ, value) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise TypeError("don't know how to create data for '{}'".format(value)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for k, v in data.items(): | 
					
						
							|  |  |  |         _create_registry_data(root, k, v) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def enum_keys(root): | 
					
						
							|  |  |  |     for i in itertools.count(): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             yield winreg.EnumKey(root, i) | 
					
						
							|  |  |  |         except OSError as ex: | 
					
						
							|  |  |  |             if ex.winerror == 259: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def delete_registry_data(root, keys): | 
					
						
							|  |  |  |     ACCESS = winreg.KEY_WRITE | winreg.KEY_ENUMERATE_SUB_KEYS | 
					
						
							|  |  |  |     for key in list(keys): | 
					
						
							|  |  |  |         with winreg.OpenKey(root, key, access=ACCESS) as hkey: | 
					
						
							|  |  |  |             delete_registry_data(hkey, enum_keys(hkey)) | 
					
						
							|  |  |  |         winreg.DeleteKey(root, key) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def is_installed(tag): | 
					
						
							|  |  |  |     key = rf"Software\Python\PythonCore\{tag}\InstallPath" | 
					
						
							|  |  |  |     for root, flag in [ | 
					
						
							|  |  |  |         (winreg.HKEY_CURRENT_USER, 0), | 
					
						
							|  |  |  |         (winreg.HKEY_LOCAL_MACHINE, winreg.KEY_WOW64_64KEY), | 
					
						
							|  |  |  |         (winreg.HKEY_LOCAL_MACHINE, winreg.KEY_WOW64_32KEY), | 
					
						
							|  |  |  |     ]: | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             winreg.CloseKey(winreg.OpenKey(root, key, access=winreg.KEY_READ | flag)) | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         except OSError: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |     return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PreservePyIni: | 
					
						
							|  |  |  |     def __init__(self, path, content): | 
					
						
							|  |  |  |         self.path = Path(path) | 
					
						
							|  |  |  |         self.content = content | 
					
						
							|  |  |  |         self._preserved = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __enter__(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self._preserved = self.path.read_bytes() | 
					
						
							|  |  |  |         except FileNotFoundError: | 
					
						
							|  |  |  |             self._preserved = None | 
					
						
							|  |  |  |         self.path.write_text(self.content, encoding="utf-16") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __exit__(self, *exc_info): | 
					
						
							|  |  |  |         if self._preserved is None: | 
					
						
							|  |  |  |             self.path.unlink() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.path.write_bytes(self._preserved) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class RunPyMixin: | 
					
						
							|  |  |  |     py_exe = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def find_py(cls): | 
					
						
							|  |  |  |         py_exe = None | 
					
						
							| 
									
										
										
										
											2022-07-26 21:24:44 +01:00
										 |  |  |         if sysconfig.is_python_build(): | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |             py_exe = Path(sys.executable).parent / PY_EXE | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             for p in os.getenv("PATH").split(";"): | 
					
						
							|  |  |  |                 if p: | 
					
						
							|  |  |  |                     py_exe = Path(p) / PY_EXE | 
					
						
							|  |  |  |                     if py_exe.is_file(): | 
					
						
							|  |  |  |                         break | 
					
						
							| 
									
										
										
										
											2022-03-30 22:18:40 +01:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 py_exe = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Test launch and check version, to exclude installs of older | 
					
						
							|  |  |  |         # releases when running outside of a source tree | 
					
						
							|  |  |  |         if py_exe: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 with subprocess.Popen( | 
					
						
							|  |  |  |                     [py_exe, "-h"], | 
					
						
							|  |  |  |                     stdin=subprocess.PIPE, | 
					
						
							|  |  |  |                     stdout=subprocess.PIPE, | 
					
						
							|  |  |  |                     stderr=subprocess.PIPE, | 
					
						
							|  |  |  |                     encoding="ascii", | 
					
						
							|  |  |  |                     errors="ignore", | 
					
						
							|  |  |  |                 ) as p: | 
					
						
							|  |  |  |                     p.stdin.close() | 
					
						
							| 
									
										
										
										
											2022-11-18 14:14:56 +00:00
										 |  |  |                     version = next(p.stdout, "\n").splitlines()[0].rpartition(" ")[2] | 
					
						
							| 
									
										
										
										
											2022-03-30 22:18:40 +01:00
										 |  |  |                     p.stdout.read() | 
					
						
							|  |  |  |                     p.wait(10) | 
					
						
							|  |  |  |                 if not sys.version.startswith(version): | 
					
						
							|  |  |  |                     py_exe = None | 
					
						
							|  |  |  |             except OSError: | 
					
						
							|  |  |  |                 py_exe = None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |         if not py_exe: | 
					
						
							|  |  |  |             raise unittest.SkipTest( | 
					
						
							|  |  |  |                 "cannot locate '{}' for test".format(PY_EXE) | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         return py_exe | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-03 22:18:51 +01:00
										 |  |  |     def get_py_exe(self): | 
					
						
							|  |  |  |         if not self.py_exe: | 
					
						
							|  |  |  |             self.py_exe = self.find_py() | 
					
						
							|  |  |  |         return self.py_exe | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-26 21:24:44 +01:00
										 |  |  |     def run_py(self, args, env=None, allow_fail=False, expect_returncode=0, argv=None): | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |         if not self.py_exe: | 
					
						
							|  |  |  |             self.py_exe = self.find_py() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-07 00:09:54 +01:00
										 |  |  |         ignore = {"VIRTUAL_ENV", "PY_PYTHON", "PY_PYTHON2", "PY_PYTHON3"} | 
					
						
							|  |  |  |         env = { | 
					
						
							|  |  |  |             **{k.upper(): v for k, v in os.environ.items() if k.upper() not in ignore}, | 
					
						
							|  |  |  |             "PYLAUNCHER_DEBUG": "1", | 
					
						
							|  |  |  |             "PYLAUNCHER_DRYRUN": "1", | 
					
						
							| 
									
										
										
										
											2023-02-01 21:06:56 +00:00
										 |  |  |             "PYLAUNCHER_LIMIT_TO_COMPANY": "", | 
					
						
							| 
									
										
										
										
											2022-08-03 22:18:51 +01:00
										 |  |  |             **{k.upper(): v for k, v in (env or {}).items()}, | 
					
						
							| 
									
										
										
										
											2022-04-07 00:09:54 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-07-26 21:24:44 +01:00
										 |  |  |         if not argv: | 
					
						
							|  |  |  |             argv = [self.py_exe, *args] | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |         with subprocess.Popen( | 
					
						
							| 
									
										
										
										
											2022-07-26 21:24:44 +01:00
										 |  |  |             argv, | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |             env=env, | 
					
						
							| 
									
										
										
										
											2022-07-26 21:24:44 +01:00
										 |  |  |             executable=self.py_exe, | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |             stdin=subprocess.PIPE, | 
					
						
							|  |  |  |             stdout=subprocess.PIPE, | 
					
						
							|  |  |  |             stderr=subprocess.PIPE, | 
					
						
							|  |  |  |         ) as p: | 
					
						
							|  |  |  |             p.stdin.close() | 
					
						
							|  |  |  |             p.wait(10) | 
					
						
							|  |  |  |             out = p.stdout.read().decode("utf-8", "replace") | 
					
						
							|  |  |  |             err = p.stderr.read().decode("ascii", "replace") | 
					
						
							|  |  |  |         if p.returncode != expect_returncode and support.verbose and not allow_fail: | 
					
						
							|  |  |  |             print("++ COMMAND ++") | 
					
						
							|  |  |  |             print([self.py_exe, *args]) | 
					
						
							|  |  |  |             print("++ STDOUT ++") | 
					
						
							|  |  |  |             print(out) | 
					
						
							|  |  |  |             print("++ STDERR ++") | 
					
						
							|  |  |  |             print(err) | 
					
						
							|  |  |  |         if allow_fail and p.returncode != expect_returncode: | 
					
						
							|  |  |  |             raise subprocess.CalledProcessError(p.returncode, [self.py_exe, *args], out, err) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.assertEqual(expect_returncode, p.returncode) | 
					
						
							|  |  |  |         data = { | 
					
						
							|  |  |  |             s.partition(":")[0]: s.partition(":")[2].lstrip() | 
					
						
							|  |  |  |             for s in err.splitlines() | 
					
						
							|  |  |  |             if not s.startswith("#") and ":" in s | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         data["stdout"] = out | 
					
						
							|  |  |  |         data["stderr"] = err | 
					
						
							|  |  |  |         return data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def py_ini(self, content): | 
					
						
							| 
									
										
										
										
											2022-08-22 12:23:27 -04:00
										 |  |  |         local_appdata = os.environ.get("LOCALAPPDATA") | 
					
						
							|  |  |  |         if not local_appdata: | 
					
						
							|  |  |  |             raise unittest.SkipTest("LOCALAPPDATA environment variable is " | 
					
						
							|  |  |  |                                     "missing or empty") | 
					
						
							|  |  |  |         return PreservePyIni(Path(local_appdata) / "py.ini", content) | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @contextlib.contextmanager | 
					
						
							|  |  |  |     def script(self, content, encoding="utf-8"): | 
					
						
							|  |  |  |         file = Path(tempfile.mktemp(dir=os.getcwd()) + ".py") | 
					
						
							|  |  |  |         file.write_text(content, encoding=encoding) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             yield file | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             file.unlink() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-19 23:45:41 +01:00
										 |  |  |     @contextlib.contextmanager | 
					
						
							| 
									
										
										
										
											2022-05-23 17:04:26 +01:00
										 |  |  |     def fake_venv(self): | 
					
						
							| 
									
										
										
										
											2022-05-19 23:45:41 +01:00
										 |  |  |         venv = Path.cwd() / "Scripts" | 
					
						
							|  |  |  |         venv.mkdir(exist_ok=True, parents=True) | 
					
						
							|  |  |  |         venv_exe = (venv / Path(sys.executable).name) | 
					
						
							|  |  |  |         venv_exe.touch() | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             yield venv_exe, {"VIRTUAL_ENV": str(venv.parent)} | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             shutil.rmtree(venv) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | class TestLauncher(unittest.TestCase, RunPyMixin): | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def setUpClass(cls): | 
					
						
							|  |  |  |         with winreg.CreateKey(winreg.HKEY_CURRENT_USER, rf"Software\Python") as key: | 
					
						
							|  |  |  |             create_registry_data(key, TEST_DATA) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if support.verbose: | 
					
						
							|  |  |  |             p = subprocess.check_output("reg query HKCU\\Software\\Python /s") | 
					
						
							| 
									
										
										
										
											2022-03-30 22:18:40 +01:00
										 |  |  |             #print(p.decode('mbcs')) | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def tearDownClass(cls): | 
					
						
							|  |  |  |         with winreg.OpenKey(winreg.HKEY_CURRENT_USER, rf"Software\Python", access=winreg.KEY_WRITE | winreg.KEY_ENUMERATE_SUB_KEYS) as key: | 
					
						
							|  |  |  |             delete_registry_data(key, TEST_DATA) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_version(self): | 
					
						
							|  |  |  |         data = self.run_py(["-0"]) | 
					
						
							|  |  |  |         self.assertEqual(self.py_exe, Path(data["argv0"])) | 
					
						
							|  |  |  |         self.assertEqual(sys.version.partition(" ")[0], data["version"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_help_option(self): | 
					
						
							|  |  |  |         data = self.run_py(["-h"]) | 
					
						
							|  |  |  |         self.assertEqual("True", data["SearchInfo.help"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_list_option(self): | 
					
						
							|  |  |  |         for opt, v1, v2 in [ | 
					
						
							|  |  |  |             ("-0", "True", "False"), | 
					
						
							|  |  |  |             ("-0p", "False", "True"), | 
					
						
							|  |  |  |             ("--list", "True", "False"), | 
					
						
							|  |  |  |             ("--list-paths", "False", "True"), | 
					
						
							|  |  |  |         ]: | 
					
						
							|  |  |  |             with self.subTest(opt): | 
					
						
							|  |  |  |                 data = self.run_py([opt]) | 
					
						
							|  |  |  |                 self.assertEqual(v1, data["SearchInfo.list"]) | 
					
						
							|  |  |  |                 self.assertEqual(v2, data["SearchInfo.listPaths"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_list(self): | 
					
						
							|  |  |  |         data = self.run_py(["--list"]) | 
					
						
							|  |  |  |         found = {} | 
					
						
							|  |  |  |         expect = {} | 
					
						
							|  |  |  |         for line in data["stdout"].splitlines(): | 
					
						
							| 
									
										
										
										
											2022-03-30 22:18:40 +01:00
										 |  |  |             m = re.match(r"\s*(.+?)\s+?(\*\s+)?(.+)$", line) | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |             if m: | 
					
						
							| 
									
										
										
										
											2022-03-30 22:18:40 +01:00
										 |  |  |                 found[m.group(1)] = m.group(3) | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |         for company in TEST_DATA: | 
					
						
							|  |  |  |             company_data = TEST_DATA[company] | 
					
						
							|  |  |  |             tags = [t for t in company_data if isinstance(company_data[t], dict)] | 
					
						
							|  |  |  |             for tag in tags: | 
					
						
							|  |  |  |                 arg = f"-V:{company}/{tag}" | 
					
						
							|  |  |  |                 expect[arg] = company_data[tag]["DisplayName"] | 
					
						
							|  |  |  |             expect.pop(f"-V:{company}/ignored", None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         actual = {k: v for k, v in found.items() if k in expect} | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self.assertDictEqual(expect, actual) | 
					
						
							|  |  |  |         except: | 
					
						
							|  |  |  |             if support.verbose: | 
					
						
							|  |  |  |                 print("*** STDOUT ***") | 
					
						
							|  |  |  |                 print(data["stdout"]) | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_list_paths(self): | 
					
						
							|  |  |  |         data = self.run_py(["--list-paths"]) | 
					
						
							|  |  |  |         found = {} | 
					
						
							|  |  |  |         expect = {} | 
					
						
							|  |  |  |         for line in data["stdout"].splitlines(): | 
					
						
							| 
									
										
										
										
											2022-03-30 22:18:40 +01:00
										 |  |  |             m = re.match(r"\s*(.+?)\s+?(\*\s+)?(.+)$", line) | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |             if m: | 
					
						
							| 
									
										
										
										
											2022-03-30 22:18:40 +01:00
										 |  |  |                 found[m.group(1)] = m.group(3) | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |         for company in TEST_DATA: | 
					
						
							|  |  |  |             company_data = TEST_DATA[company] | 
					
						
							|  |  |  |             tags = [t for t in company_data if isinstance(company_data[t], dict)] | 
					
						
							|  |  |  |             for tag in tags: | 
					
						
							|  |  |  |                 arg = f"-V:{company}/{tag}" | 
					
						
							|  |  |  |                 install = company_data[tag]["InstallPath"] | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     expect[arg] = install["ExecutablePath"] | 
					
						
							|  |  |  |                     try: | 
					
						
							|  |  |  |                         expect[arg] += " " + install["ExecutableArguments"] | 
					
						
							|  |  |  |                     except KeyError: | 
					
						
							|  |  |  |                         pass | 
					
						
							|  |  |  |                 except KeyError: | 
					
						
							|  |  |  |                     expect[arg] = str(Path(install[None]) / Path(sys.executable).name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             expect.pop(f"-V:{company}/ignored", None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         actual = {k: v for k, v in found.items() if k in expect} | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self.assertDictEqual(expect, actual) | 
					
						
							|  |  |  |         except: | 
					
						
							|  |  |  |             if support.verbose: | 
					
						
							|  |  |  |                 print("*** STDOUT ***") | 
					
						
							|  |  |  |                 print(data["stdout"]) | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_filter_to_company(self): | 
					
						
							|  |  |  |         company = "PythonTestSuite" | 
					
						
							|  |  |  |         data = self.run_py([f"-V:{company}/"]) | 
					
						
							|  |  |  |         self.assertEqual("X.Y.exe", data["LaunchCommand"]) | 
					
						
							|  |  |  |         self.assertEqual(company, data["env.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100", data["env.tag"]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-19 23:00:09 +01:00
										 |  |  |     def test_filter_to_company_with_default(self): | 
					
						
							|  |  |  |         company = "PythonTestSuite" | 
					
						
							|  |  |  |         data = self.run_py([f"-V:{company}/"], env=dict(PY_PYTHON="3.0")) | 
					
						
							|  |  |  |         self.assertEqual("X.Y.exe", data["LaunchCommand"]) | 
					
						
							|  |  |  |         self.assertEqual(company, data["env.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100", data["env.tag"]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |     def test_filter_to_tag(self): | 
					
						
							|  |  |  |         company = "PythonTestSuite" | 
					
						
							| 
									
										
										
										
											2023-04-24 17:24:49 -06:00
										 |  |  |         data = self.run_py(["-V:3.100"]) | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |         self.assertEqual("X.Y.exe", data["LaunchCommand"]) | 
					
						
							|  |  |  |         self.assertEqual(company, data["env.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100", data["env.tag"]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-24 17:24:49 -06:00
										 |  |  |         data = self.run_py(["-V:3.100-32"]) | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |         self.assertEqual("X.Y-32.exe", data["LaunchCommand"]) | 
					
						
							|  |  |  |         self.assertEqual(company, data["env.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100-32", data["env.tag"]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-24 17:24:49 -06:00
										 |  |  |         data = self.run_py(["-V:3.100-arm64"]) | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |         self.assertEqual("X.Y-arm64.exe -X fake_arg_for_test", data["LaunchCommand"]) | 
					
						
							|  |  |  |         self.assertEqual(company, data["env.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100-arm64", data["env.tag"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_filter_to_company_and_tag(self): | 
					
						
							|  |  |  |         company = "PythonTestSuite" | 
					
						
							| 
									
										
										
										
											2023-02-01 21:06:56 +00:00
										 |  |  |         data = self.run_py([f"-V:{company}/3.1"], expect_returncode=103) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         data = self.run_py([f"-V:{company}/3.100"]) | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |         self.assertEqual("X.Y.exe", data["LaunchCommand"]) | 
					
						
							|  |  |  |         self.assertEqual(company, data["env.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100", data["env.tag"]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-01 21:06:56 +00:00
										 |  |  |     def test_filter_with_single_install(self): | 
					
						
							|  |  |  |         company = "PythonTestSuite1" | 
					
						
							|  |  |  |         data = self.run_py( | 
					
						
							| 
									
										
										
										
											2023-04-24 17:24:49 -06:00
										 |  |  |             ["-V:Nonexistent"], | 
					
						
							| 
									
										
										
										
											2023-02-01 21:06:56 +00:00
										 |  |  |             env={"PYLAUNCHER_LIMIT_TO_COMPANY": company}, | 
					
						
							|  |  |  |             expect_returncode=103, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |     def test_search_major_3(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             data = self.run_py(["-3"], allow_fail=True) | 
					
						
							|  |  |  |         except subprocess.CalledProcessError: | 
					
						
							|  |  |  |             raise unittest.SkipTest("requires at least one Python 3.x install") | 
					
						
							|  |  |  |         self.assertEqual("PythonCore", data["env.company"]) | 
					
						
							|  |  |  |         self.assertTrue(data["env.tag"].startswith("3."), data["env.tag"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_search_major_3_32(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             data = self.run_py(["-3-32"], allow_fail=True) | 
					
						
							|  |  |  |         except subprocess.CalledProcessError: | 
					
						
							|  |  |  |             if not any(is_installed(f"3.{i}-32") for i in range(5, 11)): | 
					
						
							|  |  |  |                 raise unittest.SkipTest("requires at least one 32-bit Python 3.x install") | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  |         self.assertEqual("PythonCore", data["env.company"]) | 
					
						
							|  |  |  |         self.assertTrue(data["env.tag"].startswith("3."), data["env.tag"]) | 
					
						
							|  |  |  |         self.assertTrue(data["env.tag"].endswith("-32"), data["env.tag"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_search_major_2(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             data = self.run_py(["-2"], allow_fail=True) | 
					
						
							|  |  |  |         except subprocess.CalledProcessError: | 
					
						
							|  |  |  |             if not is_installed("2.7"): | 
					
						
							|  |  |  |                 raise unittest.SkipTest("requires at least one Python 2.x install") | 
					
						
							|  |  |  |         self.assertEqual("PythonCore", data["env.company"]) | 
					
						
							|  |  |  |         self.assertTrue(data["env.tag"].startswith("2."), data["env.tag"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_py_default(self): | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |         with self.py_ini(TEST_PY_DEFAULTS): | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |             data = self.run_py(["-arg"]) | 
					
						
							|  |  |  |         self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100", data["SearchInfo.tag"]) | 
					
						
							|  |  |  |         self.assertEqual("X.Y.exe -arg", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_py2_default(self): | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |         with self.py_ini(TEST_PY_DEFAULTS): | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |             data = self.run_py(["-2", "-arg"]) | 
					
						
							|  |  |  |         self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100-32", data["SearchInfo.tag"]) | 
					
						
							|  |  |  |         self.assertEqual("X.Y-32.exe -arg", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_py3_default(self): | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |         with self.py_ini(TEST_PY_DEFAULTS): | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |             data = self.run_py(["-3", "-arg"]) | 
					
						
							|  |  |  |         self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100-arm64", data["SearchInfo.tag"]) | 
					
						
							|  |  |  |         self.assertEqual("X.Y-arm64.exe -X fake_arg_for_test -arg", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-07 00:09:54 +01:00
										 |  |  |     def test_py_default_env(self): | 
					
						
							|  |  |  |         data = self.run_py(["-arg"], env=TEST_PY_ENV) | 
					
						
							|  |  |  |         self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100", data["SearchInfo.tag"]) | 
					
						
							|  |  |  |         self.assertEqual("X.Y.exe -arg", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_py2_default_env(self): | 
					
						
							|  |  |  |         data = self.run_py(["-2", "-arg"], env=TEST_PY_ENV) | 
					
						
							|  |  |  |         self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100-32", data["SearchInfo.tag"]) | 
					
						
							|  |  |  |         self.assertEqual("X.Y-32.exe -arg", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_py3_default_env(self): | 
					
						
							|  |  |  |         data = self.run_py(["-3", "-arg"], env=TEST_PY_ENV) | 
					
						
							|  |  |  |         self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100-arm64", data["SearchInfo.tag"]) | 
					
						
							|  |  |  |         self.assertEqual("X.Y-arm64.exe -X fake_arg_for_test -arg", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-18 14:14:56 +00:00
										 |  |  |     def test_py_default_short_argv0(self): | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |         with self.py_ini(TEST_PY_DEFAULTS): | 
					
						
							| 
									
										
										
										
											2022-11-18 14:14:56 +00:00
										 |  |  |             for argv0 in ['"py.exe"', 'py.exe', '"py"', 'py']: | 
					
						
							|  |  |  |                 with self.subTest(argv0): | 
					
						
							|  |  |  |                     data = self.run_py(["--version"], argv=f'{argv0} --version') | 
					
						
							|  |  |  |                     self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) | 
					
						
							|  |  |  |                     self.assertEqual("3.100", data["SearchInfo.tag"]) | 
					
						
							| 
									
										
										
										
											2023-04-24 17:24:49 -06:00
										 |  |  |                     self.assertEqual("X.Y.exe --version", data["stdout"].strip()) | 
					
						
							| 
									
										
										
										
											2022-11-18 14:14:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-07 00:09:54 +01:00
										 |  |  |     def test_py_default_in_list(self): | 
					
						
							|  |  |  |         data = self.run_py(["-0"], env=TEST_PY_ENV) | 
					
						
							|  |  |  |         default = None | 
					
						
							|  |  |  |         for line in data["stdout"].splitlines(): | 
					
						
							|  |  |  |             m = re.match(r"\s*-V:(.+?)\s+?\*\s+(.+)$", line) | 
					
						
							|  |  |  |             if m: | 
					
						
							|  |  |  |                 default = m.group(1) | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |         self.assertEqual("PythonTestSuite/3.100", default) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_virtualenv_in_list(self): | 
					
						
							| 
									
										
										
										
											2022-05-23 17:04:26 +01:00
										 |  |  |         with self.fake_venv() as (venv_exe, env): | 
					
						
							| 
									
										
										
										
											2022-05-19 23:45:41 +01:00
										 |  |  |             data = self.run_py(["-0p"], env=env) | 
					
						
							| 
									
										
										
										
											2022-04-07 00:09:54 +01:00
										 |  |  |             for line in data["stdout"].splitlines(): | 
					
						
							|  |  |  |                 m = re.match(r"\s*\*\s+(.+)$", line) | 
					
						
							|  |  |  |                 if m: | 
					
						
							|  |  |  |                     self.assertEqual(str(venv_exe), m.group(1)) | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.fail("did not find active venv path") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-19 23:45:41 +01:00
										 |  |  |             data = self.run_py(["-0"], env=env) | 
					
						
							| 
									
										
										
										
											2022-04-07 00:09:54 +01:00
										 |  |  |             for line in data["stdout"].splitlines(): | 
					
						
							|  |  |  |                 m = re.match(r"\s*\*\s+(.+)$", line) | 
					
						
							|  |  |  |                 if m: | 
					
						
							|  |  |  |                     self.assertEqual("Active venv", m.group(1)) | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.fail("did not find active venv entry") | 
					
						
							| 
									
										
										
										
											2022-05-19 23:45:41 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_virtualenv_with_env(self): | 
					
						
							| 
									
										
										
										
											2022-05-23 17:04:26 +01:00
										 |  |  |         with self.fake_venv() as (venv_exe, env): | 
					
						
							|  |  |  |             data1 = self.run_py([], env={**env, "PY_PYTHON": "PythonTestSuite/3"}) | 
					
						
							| 
									
										
										
										
											2022-05-25 22:59:33 +01:00
										 |  |  |             data2 = self.run_py(["-V:PythonTestSuite/3"], env={**env, "PY_PYTHON": "PythonTestSuite/3"}) | 
					
						
							| 
									
										
										
										
											2022-05-19 23:45:41 +01:00
										 |  |  |         # Compare stdout, because stderr goes via ascii | 
					
						
							|  |  |  |         self.assertEqual(data1["stdout"].strip(), str(venv_exe)) | 
					
						
							|  |  |  |         self.assertEqual(data1["SearchInfo.lowPriorityTag"], "True") | 
					
						
							|  |  |  |         # Ensure passing the argument doesn't trigger the same behaviour | 
					
						
							|  |  |  |         self.assertNotEqual(data2["stdout"].strip(), str(venv_exe)) | 
					
						
							|  |  |  |         self.assertNotEqual(data2["SearchInfo.lowPriorityTag"], "True") | 
					
						
							| 
									
										
										
										
											2022-04-07 00:09:54 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |     def test_py_shebang(self): | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |         with self.py_ini(TEST_PY_DEFAULTS): | 
					
						
							| 
									
										
										
										
											2022-08-03 22:18:51 +01:00
										 |  |  |             with self.script("#! /usr/bin/python -prearg") as script: | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |                 data = self.run_py([script, "-postarg"]) | 
					
						
							|  |  |  |         self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100", data["SearchInfo.tag"]) | 
					
						
							|  |  |  |         self.assertEqual(f"X.Y.exe -prearg {script} -postarg", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:05:50 +00:00
										 |  |  |     def test_python_shebang(self): | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |         with self.py_ini(TEST_PY_DEFAULTS): | 
					
						
							| 
									
										
										
										
											2022-10-31 21:05:50 +00:00
										 |  |  |             with self.script("#! python -prearg") as script: | 
					
						
							|  |  |  |                 data = self.run_py([script, "-postarg"]) | 
					
						
							|  |  |  |         self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100", data["SearchInfo.tag"]) | 
					
						
							|  |  |  |         self.assertEqual(f"X.Y.exe -prearg {script} -postarg", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |     def test_py2_shebang(self): | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |         with self.py_ini(TEST_PY_DEFAULTS): | 
					
						
							| 
									
										
										
										
											2022-08-03 22:18:51 +01:00
										 |  |  |             with self.script("#! /usr/bin/python2 -prearg") as script: | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |                 data = self.run_py([script, "-postarg"]) | 
					
						
							|  |  |  |         self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100-32", data["SearchInfo.tag"]) | 
					
						
							|  |  |  |         self.assertEqual(f"X.Y-32.exe -prearg {script} -postarg", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_py3_shebang(self): | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |         with self.py_ini(TEST_PY_DEFAULTS): | 
					
						
							| 
									
										
										
										
											2022-08-03 22:18:51 +01:00
										 |  |  |             with self.script("#! /usr/bin/python3 -prearg") as script: | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |                 data = self.run_py([script, "-postarg"]) | 
					
						
							|  |  |  |         self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100-arm64", data["SearchInfo.tag"]) | 
					
						
							|  |  |  |         self.assertEqual(f"X.Y-arm64.exe -X fake_arg_for_test -prearg {script} -postarg", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-16 10:02:22 +01:00
										 |  |  |     def test_py_shebang_nl(self): | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |         with self.py_ini(TEST_PY_DEFAULTS): | 
					
						
							| 
									
										
										
										
											2022-08-03 22:18:51 +01:00
										 |  |  |             with self.script("#! /usr/bin/python -prearg\n") as script: | 
					
						
							| 
									
										
										
										
											2022-07-16 10:02:22 +01:00
										 |  |  |                 data = self.run_py([script, "-postarg"]) | 
					
						
							|  |  |  |         self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100", data["SearchInfo.tag"]) | 
					
						
							|  |  |  |         self.assertEqual(f"X.Y.exe -prearg {script} -postarg", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_py2_shebang_nl(self): | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |         with self.py_ini(TEST_PY_DEFAULTS): | 
					
						
							| 
									
										
										
										
											2022-08-03 22:18:51 +01:00
										 |  |  |             with self.script("#! /usr/bin/python2 -prearg\n") as script: | 
					
						
							| 
									
										
										
										
											2022-07-16 10:02:22 +01:00
										 |  |  |                 data = self.run_py([script, "-postarg"]) | 
					
						
							|  |  |  |         self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100-32", data["SearchInfo.tag"]) | 
					
						
							|  |  |  |         self.assertEqual(f"X.Y-32.exe -prearg {script} -postarg", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_py3_shebang_nl(self): | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |         with self.py_ini(TEST_PY_DEFAULTS): | 
					
						
							| 
									
										
										
										
											2022-08-03 22:18:51 +01:00
										 |  |  |             with self.script("#! /usr/bin/python3 -prearg\n") as script: | 
					
						
							| 
									
										
										
										
											2022-07-16 10:02:22 +01:00
										 |  |  |                 data = self.run_py([script, "-postarg"]) | 
					
						
							|  |  |  |         self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100-arm64", data["SearchInfo.tag"]) | 
					
						
							|  |  |  |         self.assertEqual(f"X.Y-arm64.exe -X fake_arg_for_test -prearg {script} -postarg", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-26 21:24:44 +01:00
										 |  |  |     def test_py_shebang_short_argv0(self): | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |         with self.py_ini(TEST_PY_DEFAULTS): | 
					
						
							| 
									
										
										
										
											2022-08-03 22:18:51 +01:00
										 |  |  |             with self.script("#! /usr/bin/python -prearg") as script: | 
					
						
							| 
									
										
										
										
											2022-07-26 21:24:44 +01:00
										 |  |  |                 # Override argv to only pass "py.exe" as the command | 
					
						
							|  |  |  |                 data = self.run_py([script, "-postarg"], argv=f'"py.exe" "{script}" -postarg') | 
					
						
							|  |  |  |         self.assertEqual("PythonTestSuite", data["SearchInfo.company"]) | 
					
						
							|  |  |  |         self.assertEqual("3.100", data["SearchInfo.tag"]) | 
					
						
							|  |  |  |         self.assertEqual(f'X.Y.exe -prearg "{script}" -postarg', data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-05 20:06:30 +01:00
										 |  |  |     def test_py_handle_64_in_ini(self): | 
					
						
							|  |  |  |         with self.py_ini("\n".join(["[defaults]", "python=3.999-64"])): | 
					
						
							|  |  |  |             # Expect this to fail, but should get oldStyleTag flipped on | 
					
						
							|  |  |  |             data = self.run_py([], allow_fail=True, expect_returncode=103) | 
					
						
							|  |  |  |         self.assertEqual("3.999-64", data["SearchInfo.tag"]) | 
					
						
							|  |  |  |         self.assertEqual("True", data["SearchInfo.oldStyleTag"]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-03 22:18:51 +01:00
										 |  |  |     def test_search_path(self): | 
					
						
							|  |  |  |         stem = Path(sys.executable).stem | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |         with self.py_ini(TEST_PY_DEFAULTS): | 
					
						
							| 
									
										
										
										
											2022-08-03 22:18:51 +01:00
										 |  |  |             with self.script(f"#! /usr/bin/env {stem} -prearg") as script: | 
					
						
							|  |  |  |                 data = self.run_py( | 
					
						
							|  |  |  |                     [script, "-postarg"], | 
					
						
							|  |  |  |                     env={"PATH": f"{Path(sys.executable).parent};{os.getenv('PATH')}"}, | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |         self.assertEqual(f"{sys.executable} -prearg {script} -postarg", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_search_path_exe(self): | 
					
						
							|  |  |  |         # Leave the .exe on the name to ensure we don't add it a second time | 
					
						
							|  |  |  |         name = Path(sys.executable).name | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |         with self.py_ini(TEST_PY_DEFAULTS): | 
					
						
							| 
									
										
										
										
											2022-08-03 22:18:51 +01:00
										 |  |  |             with self.script(f"#! /usr/bin/env {name} -prearg") as script: | 
					
						
							|  |  |  |                 data = self.run_py( | 
					
						
							|  |  |  |                     [script, "-postarg"], | 
					
						
							|  |  |  |                     env={"PATH": f"{Path(sys.executable).parent};{os.getenv('PATH')}"}, | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |         self.assertEqual(f"{sys.executable} -prearg {script} -postarg", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_recursive_search_path(self): | 
					
						
							|  |  |  |         stem = self.get_py_exe().stem | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  |         with self.py_ini(TEST_PY_DEFAULTS): | 
					
						
							| 
									
										
										
										
											2022-08-03 22:18:51 +01:00
										 |  |  |             with self.script(f"#! /usr/bin/env {stem}") as script: | 
					
						
							|  |  |  |                 data = self.run_py( | 
					
						
							|  |  |  |                     [script], | 
					
						
							|  |  |  |                     env={"PATH": f"{self.get_py_exe().parent};{os.getenv('PATH')}"}, | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |         # The recursive search is ignored and we get normal "py" behavior | 
					
						
							|  |  |  |         self.assertEqual(f"X.Y.exe {script}", data["stdout"].strip()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |     def test_install(self): | 
					
						
							|  |  |  |         data = self.run_py(["-V:3.10"], env={"PYLAUNCHER_ALWAYS_INSTALL": "1"}, expect_returncode=111) | 
					
						
							|  |  |  |         cmd = data["stdout"].strip() | 
					
						
							|  |  |  |         # If winget is runnable, we should find it. Otherwise, we'll be trying | 
					
						
							|  |  |  |         # to open the Store. | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2022-03-30 22:18:40 +01:00
										 |  |  |             subprocess.check_call(["winget.exe", "--version"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |         except FileNotFoundError: | 
					
						
							|  |  |  |             self.assertIn("ms-windows-store://", cmd) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.assertIn("winget.exe", cmd) | 
					
						
							| 
									
										
										
										
											2022-03-30 22:18:40 +01:00
										 |  |  |         # Both command lines include the store ID | 
					
						
							| 
									
										
										
										
											2022-03-29 00:21:08 +01:00
										 |  |  |         self.assertIn("9PJPW5LDXLZ5", cmd) | 
					
						
							| 
									
										
										
										
											2022-10-31 21:05:50 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_literal_shebang_absolute(self): | 
					
						
							| 
									
										
										
										
											2023-04-24 17:24:49 -06:00
										 |  |  |         with self.script("#! C:/some_random_app -witharg") as script: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:05:50 +00:00
										 |  |  |             data = self.run_py([script]) | 
					
						
							|  |  |  |         self.assertEqual( | 
					
						
							|  |  |  |             f"C:\\some_random_app -witharg {script}", | 
					
						
							|  |  |  |             data["stdout"].strip(), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_literal_shebang_relative(self): | 
					
						
							| 
									
										
										
										
											2023-04-24 17:24:49 -06:00
										 |  |  |         with self.script("#! ..\\some_random_app -witharg") as script: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:05:50 +00:00
										 |  |  |             data = self.run_py([script]) | 
					
						
							|  |  |  |         self.assertEqual( | 
					
						
							|  |  |  |             f"{script.parent.parent}\\some_random_app -witharg {script}", | 
					
						
							|  |  |  |             data["stdout"].strip(), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_literal_shebang_quoted(self): | 
					
						
							| 
									
										
										
										
											2023-04-24 17:24:49 -06:00
										 |  |  |         with self.script('#! "some random app" -witharg') as script: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:05:50 +00:00
										 |  |  |             data = self.run_py([script]) | 
					
						
							|  |  |  |         self.assertEqual( | 
					
						
							|  |  |  |             f'"{script.parent}\\some random app" -witharg {script}', | 
					
						
							|  |  |  |             data["stdout"].strip(), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-24 17:24:49 -06:00
										 |  |  |         with self.script('#! some" random "app -witharg') as script: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:05:50 +00:00
										 |  |  |             data = self.run_py([script]) | 
					
						
							|  |  |  |         self.assertEqual( | 
					
						
							|  |  |  |             f'"{script.parent}\\some random app" -witharg {script}', | 
					
						
							|  |  |  |             data["stdout"].strip(), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_literal_shebang_quoted_escape(self): | 
					
						
							| 
									
										
										
										
											2023-04-24 17:24:49 -06:00
										 |  |  |         with self.script('#! some\\" random "app -witharg') as script: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:05:50 +00:00
										 |  |  |             data = self.run_py([script]) | 
					
						
							|  |  |  |         self.assertEqual( | 
					
						
							|  |  |  |             f'"{script.parent}\\some\\ random app" -witharg {script}', | 
					
						
							|  |  |  |             data["stdout"].strip(), | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2023-01-13 11:49:01 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_literal_shebang_command(self): | 
					
						
							|  |  |  |         with self.py_ini(TEST_PY_COMMANDS): | 
					
						
							|  |  |  |             with self.script('#! test-command arg1') as script: | 
					
						
							|  |  |  |                 data = self.run_py([script]) | 
					
						
							|  |  |  |         self.assertEqual( | 
					
						
							|  |  |  |             f"TEST_EXE.exe arg1 {script}", | 
					
						
							|  |  |  |             data["stdout"].strip(), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_literal_shebang_invalid_template(self): | 
					
						
							|  |  |  |         with self.script('#! /usr/bin/not-python arg1') as script: | 
					
						
							|  |  |  |             data = self.run_py([script]) | 
					
						
							|  |  |  |         expect = script.parent / "/usr/bin/not-python" | 
					
						
							|  |  |  |         self.assertEqual( | 
					
						
							|  |  |  |             f"{expect} arg1 {script}", | 
					
						
							|  |  |  |             data["stdout"].strip(), | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2023-10-02 13:22:55 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_shebang_command_in_venv(self): | 
					
						
							|  |  |  |         stem = "python-that-is-not-on-path" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # First ensure that our test name doesn't exist, and the launcher does | 
					
						
							|  |  |  |         # not match any installed env | 
					
						
							|  |  |  |         with self.script(f'#! /usr/bin/env {stem} arg1') as script: | 
					
						
							|  |  |  |             data = self.run_py([script], expect_returncode=103) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with self.fake_venv() as (venv_exe, env): | 
					
						
							|  |  |  |             # Put a real Python (ourselves) on PATH as a distraction. | 
					
						
							|  |  |  |             # The active VIRTUAL_ENV should be preferred when the name isn't an | 
					
						
							|  |  |  |             # exact match. | 
					
						
							|  |  |  |             env["PATH"] = f"{Path(sys.executable).parent};{os.environ['PATH']}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             with self.script(f'#! /usr/bin/env {stem} arg1') as script: | 
					
						
							|  |  |  |                 data = self.run_py([script], env=env) | 
					
						
							|  |  |  |             self.assertEqual(data["stdout"].strip(), f"{venv_exe} arg1 {script}") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             with self.script(f'#! /usr/bin/env {Path(sys.executable).stem} arg1') as script: | 
					
						
							|  |  |  |                 data = self.run_py([script], env=env) | 
					
						
							|  |  |  |             self.assertEqual(data["stdout"].strip(), f"{sys.executable} arg1 {script}") |