From c9145ea380e5685f03beca04d6b1fc0d827e0015 Mon Sep 17 00:00:00 2001 From: Charles Machalow Date: Fri, 15 Sep 2023 15:00:46 -0700 Subject: [PATCH 01/12] Add unit tests for site.{usercustomize,sitecustomize} hooks --- Lib/test/test_site.py | 38 +++++++++++++++++++ ...-09-15-15-00-14.gh-issue-108747.ql0owS.rst | 2 + 2 files changed, 40 insertions(+) create mode 100644 Misc/NEWS.d/next/Tests/2023-09-15-15-00-14.gh-issue-108747.ql0owS.rst diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index e8ec3b35881fec..f4b4337e06aff9 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -465,6 +465,44 @@ def test_sitecustomize_executed(self): else: self.fail("sitecustomize not imported automatically") + @support.requires_subprocess() + def test_customization_modules_on_startup(self): + # Check that sitecustomize and or usercustomize are executed on startup + mod_info = [ + # func to get directory, file base name + ('getusersitepackages', 'usercustomize'), + ('getsitepackages', 'sitecustomize') + ] + + for func_name, module_name in mod_info: + # getusersitepackages returns a string.. getsitepackages returns a list.. + # handle either way. + base_path = getattr(site, func_name)() + if not isinstance(base_path, str): + base_path = base_path[0] + + customize_path = os.path.join(base_path, f'{module_name}.py') + if os.path.exists(customize_path): + # backup old sitecustomize.py + oldcustomize_path = customize_path + '.old' + if os.path.exists(oldcustomize_path): + os.remove(oldcustomize_path) + os.rename(customize_path, oldcustomize_path) + self.addCleanup(os.rename, oldcustomize_path, customize_path) + + self.addCleanup(os.remove, customize_path) + + eyecatcher = 'EXECUTED' + + with open(customize_path, 'w') as f: + f.write(f'print("{eyecatcher}")') + + output = subprocess.check_output(f'{sys.executable} -c ""') + self.assertIn(eyecatcher, output.decode('utf-8')) + + output = subprocess.check_output(f'{sys.executable} -S -c ""') + self.assertNotIn(eyecatcher, output.decode('utf-8')) + @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"), 'need SSL support to download license') @test.support.requires_resource('network') diff --git a/Misc/NEWS.d/next/Tests/2023-09-15-15-00-14.gh-issue-108747.ql0owS.rst b/Misc/NEWS.d/next/Tests/2023-09-15-15-00-14.gh-issue-108747.ql0owS.rst new file mode 100644 index 00000000000000..ba1fe97871929c --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-09-15-15-00-14.gh-issue-108747.ql0owS.rst @@ -0,0 +1,2 @@ +Add unit test for ``usercustomize`` and ``sitecustomize`` hooks from +:class:`site`. From e4424116d6651e35f16806691664ddfbefacf5c6 Mon Sep 17 00:00:00 2001 From: Charles Machalow Date: Fri, 15 Sep 2023 15:06:40 -0700 Subject: [PATCH 02/12] Check -s for usercustomize --- Lib/test/test_site.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index f4b4337e06aff9..2fe8fab9dc8190 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -500,9 +500,15 @@ def test_customization_modules_on_startup(self): output = subprocess.check_output(f'{sys.executable} -c ""') self.assertIn(eyecatcher, output.decode('utf-8')) + # -S blocks any site-packages output = subprocess.check_output(f'{sys.executable} -S -c ""') self.assertNotIn(eyecatcher, output.decode('utf-8')) + # -s blocks user site-packages + if 'usercustomize' == module_name: + output = subprocess.check_output(f'{sys.executable} -s -c ""') + self.assertNotIn(eyecatcher, output.decode('utf-8')) + @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"), 'need SSL support to download license') @test.support.requires_resource('network') From fcd24db3a4404991758cef847bfb0824a960c632 Mon Sep 17 00:00:00 2001 From: Charles Machalow Date: Fri, 15 Sep 2023 15:33:17 -0700 Subject: [PATCH 03/12] Fix to not need shell on posix --- Lib/test/test_site.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 2fe8fab9dc8190..5fdea89246724d 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -497,16 +497,16 @@ def test_customization_modules_on_startup(self): with open(customize_path, 'w') as f: f.write(f'print("{eyecatcher}")') - output = subprocess.check_output(f'{sys.executable} -c ""') + output = subprocess.check_output([sys.executable, '-c', '""']) self.assertIn(eyecatcher, output.decode('utf-8')) # -S blocks any site-packages - output = subprocess.check_output(f'{sys.executable} -S -c ""') + output = subprocess.check_output([sys.executable, '-S', '-c', '""']) self.assertNotIn(eyecatcher, output.decode('utf-8')) # -s blocks user site-packages if 'usercustomize' == module_name: - output = subprocess.check_output(f'{sys.executable} -s -c ""') + output = subprocess.check_output([sys.executable, '-s', '-c', '""']) self.assertNotIn(eyecatcher, output.decode('utf-8')) @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"), From d1f75def2853acc3f7aa9da5140e48e62e9a2d6e Mon Sep 17 00:00:00 2001 From: Charles Machalow Date: Fri, 15 Sep 2023 16:21:27 -0700 Subject: [PATCH 04/12] Apparently these dirs don't have to exist --- Lib/test/test_site.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 5fdea89246724d..39f50ac22cd752 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -481,6 +481,8 @@ def test_customization_modules_on_startup(self): if not isinstance(base_path, str): base_path = base_path[0] + os.makedirs(base_path, exist_ok=True) + customize_path = os.path.join(base_path, f'{module_name}.py') if os.path.exists(customize_path): # backup old sitecustomize.py From 0b00ba370ff616292df3e180142b7c222744b06d Mon Sep 17 00:00:00 2001 From: Charles Machalow Date: Fri, 15 Sep 2023 16:40:24 -0700 Subject: [PATCH 05/12] On certain configurations, skip part of test --- Lib/test/test_site.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 39f50ac22cd752..82c89ab8857a0c 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -481,7 +481,11 @@ def test_customization_modules_on_startup(self): if not isinstance(base_path, str): base_path = base_path[0] - os.makedirs(base_path, exist_ok=True) + try: + os.makedirs(base_path, exist_ok=True) + except PermissionError: + # Can't modify system site packages depending on the system configuration + continue customize_path = os.path.join(base_path, f'{module_name}.py') if os.path.exists(customize_path): From 63907f91a37eebc55acdbf50cf7c9df64b9dd494 Mon Sep 17 00:00:00 2001 From: Charles Machalow Date: Fri, 15 Sep 2023 16:47:32 -0700 Subject: [PATCH 06/12] make eyecatcher include more info to be sure its working --- Lib/test/test_site.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 82c89ab8857a0c..b2f4d136780a7a 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -498,7 +498,7 @@ def test_customization_modules_on_startup(self): self.addCleanup(os.remove, customize_path) - eyecatcher = 'EXECUTED' + eyecatcher = f'EXECUTED_{module_name}' with open(customize_path, 'w') as f: f.write(f'print("{eyecatcher}")') From 8a14f39e76a38723fcda84b10b5367338175ed4f Mon Sep 17 00:00:00 2001 From: Charles Machalow Date: Fri, 15 Sep 2023 17:44:03 -0700 Subject: [PATCH 07/12] try again? --- Lib/test/test_site.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index b2f4d136780a7a..d2d3de42dfafe4 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -500,8 +500,12 @@ def test_customization_modules_on_startup(self): eyecatcher = f'EXECUTED_{module_name}' - with open(customize_path, 'w') as f: - f.write(f'print("{eyecatcher}")') + try: + with open(customize_path, 'w') as f: + f.write(f'print("{eyecatcher}")') + except PermissionError: + # Can't modify system site packages depending on the system configuration + continue output = subprocess.check_output([sys.executable, '-c', '""']) self.assertIn(eyecatcher, output.decode('utf-8')) From b870a6fdcf604b68520001645397401dc723fb38 Mon Sep 17 00:00:00 2001 From: Charles Machalow Date: Fri, 15 Sep 2023 18:49:40 -0700 Subject: [PATCH 08/12] try again? --- Lib/test/test_site.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index d2d3de42dfafe4..df9be3f60ef351 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -470,10 +470,12 @@ def test_customization_modules_on_startup(self): # Check that sitecustomize and or usercustomize are executed on startup mod_info = [ # func to get directory, file base name - ('getusersitepackages', 'usercustomize'), ('getsitepackages', 'sitecustomize') ] + if not sys.flags.no_user_site: + mod_info.append(('getusersitepackages', 'usercustomize'),) + for func_name, module_name in mod_info: # getusersitepackages returns a string.. getsitepackages returns a list.. # handle either way. @@ -500,12 +502,8 @@ def test_customization_modules_on_startup(self): eyecatcher = f'EXECUTED_{module_name}' - try: - with open(customize_path, 'w') as f: - f.write(f'print("{eyecatcher}")') - except PermissionError: - # Can't modify system site packages depending on the system configuration - continue + with open(customize_path, 'w') as f: + f.write(f'print("{eyecatcher}")') output = subprocess.check_output([sys.executable, '-c', '""']) self.assertIn(eyecatcher, output.decode('utf-8')) From c78aeddfeaa98da465c74b5d6a90347c3cdeaf5b Mon Sep 17 00:00:00 2001 From: Charles Machalow Date: Fri, 15 Sep 2023 20:13:09 -0700 Subject: [PATCH 09/12] Fix? --- Lib/test/test_site.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index df9be3f60ef351..17e93a29189b91 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -473,7 +473,7 @@ def test_customization_modules_on_startup(self): ('getsitepackages', 'sitecustomize') ] - if not sys.flags.no_user_site: + if site.check_enableusersite(): mod_info.append(('getusersitepackages', 'usercustomize'),) for func_name, module_name in mod_info: From 22337a9559bd215295ff17703130bb03b60f633e Mon Sep 17 00:00:00 2001 From: Charles Machalow Date: Fri, 15 Sep 2023 23:12:20 -0700 Subject: [PATCH 10/12] Fix? --- Lib/test/test_site.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 17e93a29189b91..2f6f7a9855ecdd 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -498,25 +498,26 @@ def test_customization_modules_on_startup(self): os.rename(customize_path, oldcustomize_path) self.addCleanup(os.rename, oldcustomize_path, customize_path) - self.addCleanup(os.remove, customize_path) - eyecatcher = f'EXECUTED_{module_name}' - with open(customize_path, 'w') as f: - f.write(f'print("{eyecatcher}")') - - output = subprocess.check_output([sys.executable, '-c', '""']) - self.assertIn(eyecatcher, output.decode('utf-8')) + try: + with open(customize_path, 'w') as f: + f.write(f'print("{eyecatcher}")') - # -S blocks any site-packages - output = subprocess.check_output([sys.executable, '-S', '-c', '""']) - self.assertNotIn(eyecatcher, output.decode('utf-8')) + output = subprocess.check_output([sys.executable, '-c', '""']) + self.assertIn(eyecatcher, output.decode('utf-8')) - # -s blocks user site-packages - if 'usercustomize' == module_name: - output = subprocess.check_output([sys.executable, '-s', '-c', '""']) + # -S blocks any site-packages + output = subprocess.check_output([sys.executable, '-S', '-c', '""']) self.assertNotIn(eyecatcher, output.decode('utf-8')) + # -s blocks user site-packages + if 'usercustomize' == module_name: + output = subprocess.check_output([sys.executable, '-s', '-c', '""']) + self.assertNotIn(eyecatcher, output.decode('utf-8')) + finally: + os.remove(customize_path) + @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"), 'need SSL support to download license') @test.support.requires_resource('network') From cbad68d4d24ea39c98be90f844272741784c9ab0 Mon Sep 17 00:00:00 2001 From: Charles Machalow Date: Fri, 15 Sep 2023 23:14:50 -0700 Subject: [PATCH 11/12] Fix? --- Lib/test/test_site.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 2f6f7a9855ecdd..faa6002e630834 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -473,7 +473,7 @@ def test_customization_modules_on_startup(self): ('getsitepackages', 'sitecustomize') ] - if site.check_enableusersite(): + if site.ENABLE_USER_SITE: mod_info.append(('getusersitepackages', 'usercustomize'),) for func_name, module_name in mod_info: From cc0e3bb5d10499853806874f3f7d6e27ca6f8eb8 Mon Sep 17 00:00:00 2001 From: Charles Machalow Date: Sun, 15 Oct 2023 21:07:44 -0700 Subject: [PATCH 12/12] Clean up test based off pr suggestions --- Lib/test/test_site.py | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index faa6002e630834..33d0975bda8eaa 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -467,40 +467,26 @@ def test_sitecustomize_executed(self): @support.requires_subprocess() def test_customization_modules_on_startup(self): - # Check that sitecustomize and or usercustomize are executed on startup - mod_info = [ - # func to get directory, file base name - ('getsitepackages', 'sitecustomize') + mod_names = [ + 'sitecustomize' ] if site.ENABLE_USER_SITE: - mod_info.append(('getusersitepackages', 'usercustomize'),) + mod_names.append('usercustomize') - for func_name, module_name in mod_info: - # getusersitepackages returns a string.. getsitepackages returns a list.. - # handle either way. - base_path = getattr(site, func_name)() - if not isinstance(base_path, str): - base_path = base_path[0] + temp_dir = tempfile.mkdtemp() + self.addCleanup(os_helper.rmtree, temp_dir) - try: - os.makedirs(base_path, exist_ok=True) - except PermissionError: - # Can't modify system site packages depending on the system configuration - continue + with EnvironmentVarGuard() as environ: + environ['PYTHONPATH'] = temp_dir - customize_path = os.path.join(base_path, f'{module_name}.py') - if os.path.exists(customize_path): - # backup old sitecustomize.py - oldcustomize_path = customize_path + '.old' - if os.path.exists(oldcustomize_path): - os.remove(oldcustomize_path) - os.rename(customize_path, oldcustomize_path) - self.addCleanup(os.rename, oldcustomize_path, customize_path) + for module_name in mod_names: + os_helper.rmtree(temp_dir) + os.mkdir(temp_dir) - eyecatcher = f'EXECUTED_{module_name}' + customize_path = os.path.join(temp_dir, f'{module_name}.py') + eyecatcher = f'EXECUTED_{module_name}' - try: with open(customize_path, 'w') as f: f.write(f'print("{eyecatcher}")') @@ -515,8 +501,7 @@ def test_customization_modules_on_startup(self): if 'usercustomize' == module_name: output = subprocess.check_output([sys.executable, '-s', '-c', '""']) self.assertNotIn(eyecatcher, output.decode('utf-8')) - finally: - os.remove(customize_path) + @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"), 'need SSL support to download license')