blob: b9427795ec70f7c6647290fa3e0a5f19c4dedaf8 [file] [log] [blame]
# Copyright 2024 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
""
load("@rules_testing//lib:test_suite.bzl", "test_suite")
load("@rules_testing//lib:truth.bzl", "subjects")
load("//python/private/pypi:extension.bzl", "parse_modules") # buildifier: disable=bzl-visibility
load("//python/private/pypi:whl_config_setting.bzl", "whl_config_setting") # buildifier: disable=bzl-visibility
_tests = []
def _mock_mctx(*modules, environ = {}, read = None):
return struct(
os = struct(
environ = environ,
name = "unittest",
arch = "exotic",
),
read = read or (lambda _: "simple==0.0.1 --hash=sha256:deadbeef --hash=sha256:deadbaaf"),
modules = [
struct(
name = modules[0].name,
tags = modules[0].tags,
is_root = modules[0].is_root,
),
] + [
struct(
name = mod.name,
tags = mod.tags,
is_root = False,
)
for mod in modules[1:]
],
)
def _mod(*, name, parse = [], override = [], whl_mods = [], is_root = True):
return struct(
name = name,
tags = struct(
parse = parse,
override = override,
whl_mods = whl_mods,
),
is_root = is_root,
)
def _parse_modules(env, **kwargs):
return env.expect.that_struct(
parse_modules(**kwargs),
attrs = dict(
is_reproducible = subjects.bool,
exposed_packages = subjects.dict,
hub_group_map = subjects.dict,
hub_whl_map = subjects.dict,
whl_libraries = subjects.dict,
whl_mods = subjects.dict,
),
)
def _parse(
*,
hub_name,
python_version,
_evaluate_markers_srcs = [],
auth_patterns = {},
download_only = False,
enable_implicit_namespace_pkgs = False,
environment = {},
envsubst = {},
experimental_index_url = "",
experimental_requirement_cycles = {},
experimental_target_platforms = [],
extra_hub_aliases = {},
extra_pip_args = [],
isolated = True,
netrc = None,
parse_all_requirements_files = True,
pip_data_exclude = None,
python_interpreter = None,
python_interpreter_target = None,
quiet = True,
requirements_by_platform = {},
requirements_darwin = None,
requirements_linux = None,
requirements_lock = None,
requirements_windows = None,
timeout = 600,
whl_modifications = {},
**kwargs):
return struct(
_evaluate_markers_srcs = _evaluate_markers_srcs,
auth_patterns = auth_patterns,
download_only = download_only,
enable_implicit_namespace_pkgs = enable_implicit_namespace_pkgs,
environment = environment,
envsubst = envsubst,
experimental_index_url = experimental_index_url,
experimental_requirement_cycles = experimental_requirement_cycles,
experimental_target_platforms = experimental_target_platforms,
extra_hub_aliases = extra_hub_aliases,
extra_pip_args = extra_pip_args,
hub_name = hub_name,
isolated = isolated,
netrc = netrc,
parse_all_requirements_files = parse_all_requirements_files,
pip_data_exclude = pip_data_exclude,
python_interpreter = python_interpreter,
python_interpreter_target = python_interpreter_target,
python_version = python_version,
quiet = quiet,
requirements_by_platform = requirements_by_platform,
requirements_darwin = requirements_darwin,
requirements_linux = requirements_linux,
requirements_lock = requirements_lock,
requirements_windows = requirements_windows,
timeout = timeout,
whl_modifications = whl_modifications,
# The following are covered by other unit tests
experimental_extra_index_urls = [],
parallel_download = False,
experimental_index_url_overrides = {},
**kwargs
)
def _test_simple(env):
pypi = _parse_modules(
env,
module_ctx = _mock_mctx(
_mod(
name = "rules_python",
parse = [
_parse(
hub_name = "pypi",
python_version = "3.15",
requirements_lock = "requirements.txt",
),
],
),
),
available_interpreters = {
"python_3_15_host": "unit_test_interpreter_target",
},
)
pypi.is_reproducible().equals(True)
pypi.exposed_packages().contains_exactly({"pypi": ["simple"]})
pypi.hub_group_map().contains_exactly({"pypi": {}})
pypi.hub_whl_map().contains_exactly({"pypi": {
"simple": {
"pypi_315_simple": [
whl_config_setting(
version = "3.15",
),
],
},
}})
pypi.whl_libraries().contains_exactly({
"pypi_315_simple": {
"dep_template": "@pypi//{name}:{target}",
"python_interpreter_target": "unit_test_interpreter_target",
"repo": "pypi_315",
"requirement": "simple==0.0.1 --hash=sha256:deadbeef --hash=sha256:deadbaaf",
},
})
pypi.whl_mods().contains_exactly({})
_tests.append(_test_simple)
def _test_simple_multiple_requirements(env):
pypi = _parse_modules(
env,
module_ctx = _mock_mctx(
_mod(
name = "rules_python",
parse = [
_parse(
hub_name = "pypi",
python_version = "3.15",
requirements_darwin = "darwin.txt",
requirements_windows = "win.txt",
),
],
),
read = lambda x: {
"darwin.txt": "simple==0.0.2 --hash=sha256:deadb00f",
"win.txt": "simple==0.0.1 --hash=sha256:deadbeef",
}[x],
),
available_interpreters = {
"python_3_15_host": "unit_test_interpreter_target",
},
)
pypi.is_reproducible().equals(True)
pypi.exposed_packages().contains_exactly({"pypi": ["simple"]})
pypi.hub_group_map().contains_exactly({"pypi": {}})
pypi.hub_whl_map().contains_exactly({"pypi": {
"simple": {
"pypi_315_simple_osx_aarch64_osx_x86_64": [
whl_config_setting(
target_platforms = [
"cp315_osx_aarch64",
"cp315_osx_x86_64",
],
version = "3.15",
),
],
"pypi_315_simple_windows_x86_64": [
whl_config_setting(
target_platforms = [
"cp315_windows_x86_64",
],
version = "3.15",
),
],
},
}})
pypi.whl_libraries().contains_exactly({
"pypi_315_simple_osx_aarch64_osx_x86_64": {
"dep_template": "@pypi//{name}:{target}",
"python_interpreter_target": "unit_test_interpreter_target",
"repo": "pypi_315",
"requirement": "simple==0.0.2 --hash=sha256:deadb00f",
},
"pypi_315_simple_windows_x86_64": {
"dep_template": "@pypi//{name}:{target}",
"python_interpreter_target": "unit_test_interpreter_target",
"repo": "pypi_315",
"requirement": "simple==0.0.1 --hash=sha256:deadbeef",
},
})
pypi.whl_mods().contains_exactly({})
_tests.append(_test_simple_multiple_requirements)
def _test_simple_with_markers(env):
pypi = _parse_modules(
env,
module_ctx = _mock_mctx(
_mod(
name = "rules_python",
parse = [
_parse(
hub_name = "pypi",
python_version = "3.15",
requirements_lock = "universal.txt",
),
],
),
read = lambda x: {
"universal.txt": """\
torch==2.4.1+cpu ; platform_machine == 'x86_64'
torch==2.4.1 ; platform_machine != 'x86_64'
""",
}[x],
),
available_interpreters = {
"python_3_15_host": "unit_test_interpreter_target",
},
evaluate_markers = lambda _, requirements, **__: {
key: [
platform
for platform in platforms
if ("x86_64" in platform and "platform_machine ==" in key) or ("x86_64" not in platform and "platform_machine !=" in key)
]
for key, platforms in requirements.items()
},
)
pypi.is_reproducible().equals(True)
pypi.exposed_packages().contains_exactly({"pypi": ["torch"]})
pypi.hub_group_map().contains_exactly({"pypi": {}})
pypi.hub_whl_map().contains_exactly({"pypi": {
"torch": {
"pypi_315_torch_linux_aarch64_linux_arm_linux_ppc_linux_s390x_osx_aarch64": [
whl_config_setting(
target_platforms = [
"cp315_linux_aarch64",
"cp315_linux_arm",
"cp315_linux_ppc",
"cp315_linux_s390x",
"cp315_osx_aarch64",
],
version = "3.15",
),
],
"pypi_315_torch_linux_x86_64_osx_x86_64_windows_x86_64": [
whl_config_setting(
target_platforms = [
"cp315_linux_x86_64",
"cp315_osx_x86_64",
"cp315_windows_x86_64",
],
version = "3.15",
),
],
},
}})
pypi.whl_libraries().contains_exactly({
"pypi_315_torch_linux_aarch64_linux_arm_linux_ppc_linux_s390x_osx_aarch64": {
"dep_template": "@pypi//{name}:{target}",
"python_interpreter_target": "unit_test_interpreter_target",
"repo": "pypi_315",
"requirement": "torch==2.4.1 ; platform_machine != 'x86_64'",
},
"pypi_315_torch_linux_x86_64_osx_x86_64_windows_x86_64": {
"dep_template": "@pypi//{name}:{target}",
"python_interpreter_target": "unit_test_interpreter_target",
"repo": "pypi_315",
"requirement": "torch==2.4.1+cpu ; platform_machine == 'x86_64'",
},
})
pypi.whl_mods().contains_exactly({})
_tests.append(_test_simple_with_markers)
def _test_download_only_multiple(env):
pypi = _parse_modules(
env,
module_ctx = _mock_mctx(
_mod(
name = "rules_python",
parse = [
_parse(
hub_name = "pypi",
python_version = "3.15",
download_only = True,
requirements_by_platform = {
"requirements.linux_x86_64.txt": "linux_x86_64",
"requirements.osx_aarch64.txt": "osx_aarch64",
},
),
],
),
read = lambda x: {
"requirements.linux_x86_64.txt": """\
--platform=manylinux_2_17_x86_64
--python-version=315
--implementation=cp
--abi=cp315
simple==0.0.1 --hash=sha256:deadbeef
extra==0.0.1 --hash=sha256:deadb00f
""",
"requirements.osx_aarch64.txt": """\
--platform=macosx_10_9_arm64
--python-version=315
--implementation=cp
--abi=cp315
simple==0.0.3 --hash=sha256:deadbaaf
""",
}[x],
),
available_interpreters = {
"python_3_15_host": "unit_test_interpreter_target",
},
)
pypi.is_reproducible().equals(True)
pypi.exposed_packages().contains_exactly({"pypi": ["simple"]})
pypi.hub_group_map().contains_exactly({"pypi": {}})
pypi.hub_whl_map().contains_exactly({"pypi": {
"extra": {
"pypi_315_extra": [
whl_config_setting(version = "3.15"),
],
},
"simple": {
"pypi_315_simple_linux_x86_64": [
whl_config_setting(
target_platforms = ["cp315_linux_x86_64"],
version = "3.15",
),
],
"pypi_315_simple_osx_aarch64": [
whl_config_setting(
target_platforms = ["cp315_osx_aarch64"],
version = "3.15",
),
],
},
}})
pypi.whl_libraries().contains_exactly({
"pypi_315_extra": {
"dep_template": "@pypi//{name}:{target}",
"download_only": True,
"experimental_target_platforms": ["cp315_linux_x86_64"],
"extra_pip_args": ["--platform=manylinux_2_17_x86_64", "--python-version=315", "--implementation=cp", "--abi=cp315"],
"python_interpreter_target": "unit_test_interpreter_target",
"repo": "pypi_315",
"requirement": "extra==0.0.1 --hash=sha256:deadb00f",
},
"pypi_315_simple_linux_x86_64": {
"dep_template": "@pypi//{name}:{target}",
"download_only": True,
"experimental_target_platforms": ["cp315_linux_x86_64"],
"extra_pip_args": ["--platform=manylinux_2_17_x86_64", "--python-version=315", "--implementation=cp", "--abi=cp315"],
"python_interpreter_target": "unit_test_interpreter_target",
"repo": "pypi_315",
"requirement": "simple==0.0.1 --hash=sha256:deadbeef",
},
"pypi_315_simple_osx_aarch64": {
"dep_template": "@pypi//{name}:{target}",
"download_only": True,
"experimental_target_platforms": ["cp315_osx_aarch64"],
"extra_pip_args": ["--platform=macosx_10_9_arm64", "--python-version=315", "--implementation=cp", "--abi=cp315"],
"python_interpreter_target": "unit_test_interpreter_target",
"repo": "pypi_315",
"requirement": "simple==0.0.3 --hash=sha256:deadbaaf",
},
})
pypi.whl_mods().contains_exactly({})
_tests.append(_test_download_only_multiple)
def _test_simple_get_index(env):
got_simpleapi_download_args = []
got_simpleapi_download_kwargs = {}
def mocksimpleapi_download(*args, **kwargs):
got_simpleapi_download_args.extend(args)
got_simpleapi_download_kwargs.update(kwargs)
return {
"simple": struct(
whls = {
"deadb00f": struct(
yanked = False,
filename = "simple-0.0.1-py3-none-any.whl",
sha256 = "deadb00f",
url = "example2.org",
),
},
sdists = {
"deadbeef": struct(
yanked = False,
filename = "simple-0.0.1.tar.gz",
sha256 = "deadbeef",
url = "example.org",
),
},
),
}
pypi = _parse_modules(
env,
module_ctx = _mock_mctx(
_mod(
name = "rules_python",
parse = [
_parse(
hub_name = "pypi",
python_version = "3.15",
requirements_lock = "requirements.txt",
experimental_index_url = "pypi.org",
extra_pip_args = [
"--extra-args-for-sdist-building",
],
),
],
),
read = lambda x: {
"requirements.txt": """
simple==0.0.1 --hash=sha256:deadbeef --hash=sha256:deadb00f
some_pkg==0.0.1
""",
}[x],
),
available_interpreters = {
"python_3_15_host": "unit_test_interpreter_target",
},
simpleapi_download = mocksimpleapi_download,
)
pypi.is_reproducible().equals(False)
pypi.exposed_packages().contains_exactly({"pypi": ["simple", "some_pkg"]})
pypi.hub_group_map().contains_exactly({"pypi": {}})
pypi.hub_whl_map().contains_exactly({
"pypi": {
"simple": {
"pypi_315_simple_py3_none_any_deadb00f": [
whl_config_setting(
filename = "simple-0.0.1-py3-none-any.whl",
version = "3.15",
),
],
"pypi_315_simple_sdist_deadbeef": [
whl_config_setting(
filename = "simple-0.0.1.tar.gz",
version = "3.15",
),
],
},
"some_pkg": {
"pypi_315_some_pkg": [whl_config_setting(version = "3.15")],
},
},
})
pypi.whl_libraries().contains_exactly({
"pypi_315_simple_py3_none_any_deadb00f": {
"dep_template": "@pypi//{name}:{target}",
"experimental_target_platforms": [
"cp315_linux_aarch64",
"cp315_linux_arm",
"cp315_linux_ppc",
"cp315_linux_s390x",
"cp315_linux_x86_64",
"cp315_osx_aarch64",
"cp315_osx_x86_64",
"cp315_windows_x86_64",
],
"filename": "simple-0.0.1-py3-none-any.whl",
"python_interpreter_target": "unit_test_interpreter_target",
"repo": "pypi_315",
"requirement": "simple==0.0.1",
"sha256": "deadb00f",
"urls": ["example2.org"],
},
"pypi_315_simple_sdist_deadbeef": {
"dep_template": "@pypi//{name}:{target}",
"experimental_target_platforms": [
"cp315_linux_aarch64",
"cp315_linux_arm",
"cp315_linux_ppc",
"cp315_linux_s390x",
"cp315_linux_x86_64",
"cp315_osx_aarch64",
"cp315_osx_x86_64",
"cp315_windows_x86_64",
],
"extra_pip_args": ["--extra-args-for-sdist-building"],
"filename": "simple-0.0.1.tar.gz",
"python_interpreter_target": "unit_test_interpreter_target",
"repo": "pypi_315",
"requirement": "simple==0.0.1",
"sha256": "deadbeef",
"urls": ["example.org"],
},
# We are falling back to regular `pip`
"pypi_315_some_pkg": {
"dep_template": "@pypi//{name}:{target}",
"extra_pip_args": ["--extra-args-for-sdist-building"],
"python_interpreter_target": "unit_test_interpreter_target",
"repo": "pypi_315",
"requirement": "some_pkg==0.0.1",
},
})
pypi.whl_mods().contains_exactly({})
env.expect.that_dict(got_simpleapi_download_kwargs).contains_exactly({
"attr": struct(
auth_patterns = {},
envsubst = {},
extra_index_urls = [],
index_url = "pypi.org",
index_url_overrides = {},
netrc = None,
sources = ["simple", "some_pkg"],
),
"cache": {},
"parallel_download": False,
})
_tests.append(_test_simple_get_index)
def extension_test_suite(name):
"""Create the test suite.
Args:
name: the name of the test suite
"""
test_suite(name = name, basic_tests = _tests)