1# Copyright 2024 The Bazel Authors. All rights reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import os.path 16import re 17import sys 18import unittest 19 20 21class SysPathOrderTest(unittest.TestCase): 22 def test_sys_path_order(self): 23 last_stdlib = None 24 first_user = None 25 first_runtime_site = None 26 27 # Classify paths into the three different types we care about: stdlib, 28 # user dependency, or the runtime's site-package's directory. 29 # 30 # Because they often share common prefixes with one another, and vary 31 # subtly between platforms, we do this in two passes: first categorize, 32 # then pick out the indexes. This is just so debugging is easier and 33 # error messages are more informative. 34 categorized_paths = [] 35 for i, value in enumerate(sys.path): 36 # The runtime's root repo may be added to sys.path, but it 37 # counts as a user directory, not stdlib directory. 38 if value == sys.prefix: 39 category = "user" 40 elif value.startswith(sys.prefix): 41 # The runtime's site-package directory might be called 42 # dist-packages when using Debian's system python. 43 if os.path.basename(value).endswith("-packages"): 44 category = "runtime-site" 45 else: 46 category = "stdlib" 47 else: 48 category = "user" 49 50 categorized_paths.append((category, value)) 51 52 for i, (category, _) in enumerate(categorized_paths): 53 if category == "stdlib": 54 last_stdlib = i 55 elif category == "runtime-site": 56 if first_runtime_site is None: 57 first_runtime_site = i 58 elif category == "user": 59 if first_user is None: 60 first_user = i 61 62 sys_path_str = "\n".join( 63 f"{i}: ({category}) {value}" 64 for i, (category, value) in enumerate(categorized_paths) 65 ) 66 if None in (last_stdlib, first_user, first_runtime_site): 67 self.fail( 68 "Failed to find position for one of:\n" 69 + f"{last_stdlib=} {first_user=} {first_runtime_site=}\n" 70 + f"for sys.path:\n{sys_path_str}" 71 ) 72 73 if os.environ["BOOTSTRAP"] == "script": 74 self.assertTrue( 75 last_stdlib < first_user < first_runtime_site, 76 f"Expected {last_stdlib=} < {first_user=} < {first_runtime_site=}\n" 77 + f"for sys.path:\n{sys_path_str}", 78 ) 79 else: 80 self.assertTrue( 81 first_user < last_stdlib < first_runtime_site, 82 f"Expected {first_user=} < {last_stdlib=} < {first_runtime_site=}\n" 83 + f"for sys.path:\n{sys_path_str}", 84 ) 85 86 87if __name__ == "__main__": 88 unittest.main() 89