xref: /aosp_15_r20/external/bazelbuild-rules_python/tests/bootstrap_impls/sys_path_order_test.py (revision 60517a1edbc8ecf509223e9af94a7adec7d736b8)
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