• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
15""
16
17load("@rules_testing//lib:test_suite.bzl", "test_suite")
18load("//python/private/pypi:parse_requirements.bzl", "parse_requirements", "select_requirement")  # buildifier: disable=bzl-visibility
19
20def _mock_ctx():
21    testdata = {
22        "requirements_different_package_version": """\
23foo==0.0.1+local --hash=sha256:deadbeef
24foo==0.0.1 --hash=sha256:deadb00f
25""",
26        "requirements_direct": """\
27foo[extra] @ https://some-url
28""",
29        "requirements_extra_args": """\
30--index-url=example.org
31
32foo[extra]==0.0.1 --hash=sha256:deadbeef
33""",
34        "requirements_linux": """\
35foo==0.0.3 --hash=sha256:deadbaaf
36""",
37        # download_only = True
38        "requirements_linux_download_only": """\
39--platform=manylinux_2_17_x86_64
40--python-version=39
41--implementation=cp
42--abi=cp39
43
44foo==0.0.1 --hash=sha256:deadbeef
45bar==0.0.1 --hash=sha256:deadb00f
46""",
47        "requirements_lock": """\
48foo[extra]==0.0.1 --hash=sha256:deadbeef
49""",
50        "requirements_lock_dupe": """\
51foo[extra,extra_2]==0.0.1 --hash=sha256:deadbeef
52foo==0.0.1 --hash=sha256:deadbeef
53foo[extra]==0.0.1 --hash=sha256:deadbeef
54""",
55        "requirements_marker": """\
56foo[extra]==0.0.1 ;marker --hash=sha256:deadbeef
57bar==0.0.1 --hash=sha256:deadbeef
58""",
59        "requirements_osx": """\
60foo==0.0.3 --hash=sha256:deadbaaf
61""",
62        "requirements_osx_download_only": """\
63--platform=macosx_10_9_arm64
64--python-version=39
65--implementation=cp
66--abi=cp39
67
68foo==0.0.3 --hash=sha256:deadbaaf
69""",
70        "requirements_windows": """\
71foo[extra]==0.0.2 --hash=sha256:deadbeef
72bar==0.0.1 --hash=sha256:deadb00f
73""",
74    }
75
76    return struct(
77        os = struct(
78            name = "linux",
79            arch = "x86_64",
80        ),
81        read = lambda x: testdata[x],
82    )
83
84_tests = []
85
86def _test_simple(env):
87    got = parse_requirements(
88        ctx = _mock_ctx(),
89        requirements_by_platform = {
90            "requirements_lock": ["linux_x86_64", "windows_x86_64"],
91        },
92    )
93    env.expect.that_dict(got).contains_exactly({
94        "foo": [
95            struct(
96                distribution = "foo",
97                extra_pip_args = [],
98                requirement_line = "foo[extra]==0.0.1 --hash=sha256:deadbeef",
99                srcs = struct(
100                    requirement = "foo[extra]==0.0.1",
101                    shas = ["deadbeef"],
102                    version = "0.0.1",
103                ),
104                target_platforms = [
105                    "linux_x86_64",
106                    "windows_x86_64",
107                ],
108                whls = [],
109                sdist = None,
110                is_exposed = True,
111            ),
112        ],
113    })
114    env.expect.that_str(
115        select_requirement(
116            got["foo"],
117            platform = "linux_x86_64",
118        ).srcs.version,
119    ).equals("0.0.1")
120
121_tests.append(_test_simple)
122
123def _test_extra_pip_args(env):
124    got = parse_requirements(
125        ctx = _mock_ctx(),
126        requirements_by_platform = {
127            "requirements_extra_args": ["linux_x86_64"],
128        },
129        extra_pip_args = ["--trusted-host=example.org"],
130    )
131    env.expect.that_dict(got).contains_exactly({
132        "foo": [
133            struct(
134                distribution = "foo",
135                extra_pip_args = ["--index-url=example.org", "--trusted-host=example.org"],
136                requirement_line = "foo[extra]==0.0.1 --hash=sha256:deadbeef",
137                srcs = struct(
138                    requirement = "foo[extra]==0.0.1",
139                    shas = ["deadbeef"],
140                    version = "0.0.1",
141                ),
142                target_platforms = [
143                    "linux_x86_64",
144                ],
145                whls = [],
146                sdist = None,
147                is_exposed = True,
148            ),
149        ],
150    })
151    env.expect.that_str(
152        select_requirement(
153            got["foo"],
154            platform = "linux_x86_64",
155        ).srcs.version,
156    ).equals("0.0.1")
157
158_tests.append(_test_extra_pip_args)
159
160def _test_dupe_requirements(env):
161    got = parse_requirements(
162        ctx = _mock_ctx(),
163        requirements_by_platform = {
164            "requirements_lock_dupe": ["linux_x86_64"],
165        },
166    )
167    env.expect.that_dict(got).contains_exactly({
168        "foo": [
169            struct(
170                distribution = "foo",
171                extra_pip_args = [],
172                requirement_line = "foo[extra,extra_2]==0.0.1 --hash=sha256:deadbeef",
173                srcs = struct(
174                    requirement = "foo[extra,extra_2]==0.0.1",
175                    shas = ["deadbeef"],
176                    version = "0.0.1",
177                ),
178                target_platforms = ["linux_x86_64"],
179                whls = [],
180                sdist = None,
181                is_exposed = True,
182            ),
183        ],
184    })
185
186_tests.append(_test_dupe_requirements)
187
188def _test_multi_os(env):
189    got = parse_requirements(
190        ctx = _mock_ctx(),
191        requirements_by_platform = {
192            "requirements_linux": ["linux_x86_64"],
193            "requirements_windows": ["windows_x86_64"],
194        },
195    )
196
197    env.expect.that_dict(got).contains_exactly({
198        "bar": [
199            struct(
200                distribution = "bar",
201                extra_pip_args = [],
202                requirement_line = "bar==0.0.1 --hash=sha256:deadb00f",
203                srcs = struct(
204                    requirement = "bar==0.0.1",
205                    shas = ["deadb00f"],
206                    version = "0.0.1",
207                ),
208                target_platforms = ["windows_x86_64"],
209                whls = [],
210                sdist = None,
211                is_exposed = False,
212            ),
213        ],
214        "foo": [
215            struct(
216                distribution = "foo",
217                extra_pip_args = [],
218                requirement_line = "foo==0.0.3 --hash=sha256:deadbaaf",
219                srcs = struct(
220                    requirement = "foo==0.0.3",
221                    shas = ["deadbaaf"],
222                    version = "0.0.3",
223                ),
224                target_platforms = ["linux_x86_64"],
225                whls = [],
226                sdist = None,
227                is_exposed = True,
228            ),
229            struct(
230                distribution = "foo",
231                extra_pip_args = [],
232                requirement_line = "foo[extra]==0.0.2 --hash=sha256:deadbeef",
233                srcs = struct(
234                    requirement = "foo[extra]==0.0.2",
235                    shas = ["deadbeef"],
236                    version = "0.0.2",
237                ),
238                target_platforms = ["windows_x86_64"],
239                whls = [],
240                sdist = None,
241                is_exposed = True,
242            ),
243        ],
244    })
245    env.expect.that_str(
246        select_requirement(
247            got["foo"],
248            platform = "windows_x86_64",
249        ).srcs.version,
250    ).equals("0.0.2")
251
252_tests.append(_test_multi_os)
253
254def _test_multi_os_legacy(env):
255    got = parse_requirements(
256        ctx = _mock_ctx(),
257        requirements_by_platform = {
258            "requirements_linux_download_only": ["cp39_linux_x86_64"],
259            "requirements_osx_download_only": ["cp39_osx_aarch64"],
260        },
261    )
262
263    env.expect.that_dict(got).contains_exactly({
264        "bar": [
265            struct(
266                distribution = "bar",
267                extra_pip_args = ["--platform=manylinux_2_17_x86_64", "--python-version=39", "--implementation=cp", "--abi=cp39"],
268                is_exposed = False,
269                requirement_line = "bar==0.0.1 --hash=sha256:deadb00f",
270                sdist = None,
271                srcs = struct(
272                    requirement = "bar==0.0.1",
273                    shas = ["deadb00f"],
274                    version = "0.0.1",
275                ),
276                target_platforms = ["cp39_linux_x86_64"],
277                whls = [],
278            ),
279        ],
280        "foo": [
281            struct(
282                distribution = "foo",
283                extra_pip_args = ["--platform=manylinux_2_17_x86_64", "--python-version=39", "--implementation=cp", "--abi=cp39"],
284                is_exposed = True,
285                requirement_line = "foo==0.0.1 --hash=sha256:deadbeef",
286                sdist = None,
287                srcs = struct(
288                    requirement = "foo==0.0.1",
289                    shas = ["deadbeef"],
290                    version = "0.0.1",
291                ),
292                target_platforms = ["cp39_linux_x86_64"],
293                whls = [],
294            ),
295            struct(
296                distribution = "foo",
297                extra_pip_args = ["--platform=macosx_10_9_arm64", "--python-version=39", "--implementation=cp", "--abi=cp39"],
298                is_exposed = True,
299                requirement_line = "foo==0.0.3 --hash=sha256:deadbaaf",
300                sdist = None,
301                srcs = struct(
302                    requirement = "foo==0.0.3",
303                    shas = ["deadbaaf"],
304                    version = "0.0.3",
305                ),
306                target_platforms = ["cp39_osx_aarch64"],
307                whls = [],
308            ),
309        ],
310    })
311
312_tests.append(_test_multi_os_legacy)
313
314def _test_select_requirement_none_platform(env):
315    got = select_requirement(
316        [
317            struct(
318                some_attr = "foo",
319                target_platforms = ["linux_x86_64"],
320            ),
321        ],
322        platform = None,
323    )
324    env.expect.that_str(got.some_attr).equals("foo")
325
326_tests.append(_test_select_requirement_none_platform)
327
328def _test_env_marker_resolution(env):
329    def _mock_eval_markers(_, input):
330        ret = {
331            "foo[extra]==0.0.1 ;marker --hash=sha256:deadbeef": ["cp311_windows_x86_64"],
332        }
333
334        env.expect.that_collection(input.keys()).contains_exactly(ret.keys())
335        env.expect.that_collection(input.values()[0]).contains_exactly(["cp311_linux_super_exotic", "cp311_windows_x86_64"])
336        return ret
337
338    got = parse_requirements(
339        ctx = _mock_ctx(),
340        requirements_by_platform = {
341            "requirements_marker": ["cp311_linux_super_exotic", "cp311_windows_x86_64"],
342        },
343        evaluate_markers = _mock_eval_markers,
344    )
345    env.expect.that_dict(got).contains_exactly({
346        "bar": [
347            struct(
348                distribution = "bar",
349                extra_pip_args = [],
350                is_exposed = True,
351                requirement_line = "bar==0.0.1 --hash=sha256:deadbeef",
352                sdist = None,
353                srcs = struct(
354                    requirement = "bar==0.0.1",
355                    shas = ["deadbeef"],
356                    version = "0.0.1",
357                ),
358                target_platforms = ["cp311_linux_super_exotic", "cp311_windows_x86_64"],
359                whls = [],
360            ),
361        ],
362        "foo": [
363            struct(
364                distribution = "foo",
365                extra_pip_args = [],
366                # This is not exposed because we also have `linux_super_exotic` in the platform list
367                is_exposed = False,
368                requirement_line = "foo[extra]==0.0.1 ;marker --hash=sha256:deadbeef",
369                sdist = None,
370                srcs = struct(
371                    requirement = "foo[extra]==0.0.1 ;marker",
372                    shas = ["deadbeef"],
373                    version = "0.0.1",
374                ),
375                target_platforms = ["cp311_windows_x86_64"],
376                whls = [],
377            ),
378        ],
379    })
380    env.expect.that_str(
381        select_requirement(
382            got["foo"],
383            platform = "windows_x86_64",
384        ).srcs.version,
385    ).equals("0.0.1")
386
387_tests.append(_test_env_marker_resolution)
388
389def _test_different_package_version(env):
390    got = parse_requirements(
391        ctx = _mock_ctx(),
392        requirements_by_platform = {
393            "requirements_different_package_version": ["linux_x86_64"],
394        },
395    )
396    env.expect.that_dict(got).contains_exactly({
397        "foo": [
398            struct(
399                distribution = "foo",
400                extra_pip_args = [],
401                requirement_line = "foo==0.0.1 --hash=sha256:deadb00f",
402                srcs = struct(
403                    requirement = "foo==0.0.1",
404                    shas = ["deadb00f"],
405                    version = "0.0.1",
406                ),
407                target_platforms = ["linux_x86_64"],
408                whls = [],
409                sdist = None,
410                is_exposed = True,
411            ),
412            struct(
413                distribution = "foo",
414                extra_pip_args = [],
415                requirement_line = "foo==0.0.1+local --hash=sha256:deadbeef",
416                srcs = struct(
417                    requirement = "foo==0.0.1+local",
418                    shas = ["deadbeef"],
419                    version = "0.0.1+local",
420                ),
421                target_platforms = ["linux_x86_64"],
422                whls = [],
423                sdist = None,
424                is_exposed = True,
425            ),
426        ],
427    })
428
429_tests.append(_test_different_package_version)
430
431def parse_requirements_test_suite(name):
432    """Create the test suite.
433
434    Args:
435        name: the name of the test suite
436    """
437    test_suite(name = name, basic_tests = _tests)
438