diff --git a/.bazelrc.deleted_packages b/.bazelrc.deleted_packages index d11f96d664..5c8eea7ffd 100644 --- a/.bazelrc.deleted_packages +++ b/.bazelrc.deleted_packages @@ -52,3 +52,7 @@ common --deleted_packages=tests/modules/other/nspkg_single common --deleted_packages=tests/modules/other/simple_v1 common --deleted_packages=tests/modules/other/simple_v2 common --deleted_packages=tests/modules/other/with_external_data +common --deleted_packages=tests/pyproject/compile_requirements_test +common --deleted_packages=tests/pyproject/pip_integration_test +common --deleted_packages=tests/pyproject/priority_test +common --deleted_packages=tests/pyproject/python_toolchain_test diff --git a/.gitattributes b/.gitattributes index eae260e931..82cbc9c0a8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ python/features.bzl export-subst tools/publish/*.txt linguist-generated=true +requirements_lock.txt linguist-generated=true diff --git a/.gitignore b/.gitignore index fb1b17e466..bd64959c44 100644 --- a/.gitignore +++ b/.gitignore @@ -48,8 +48,10 @@ user.bazelrc # CLion .clwb -# Python cache +# Python artifacts **/__pycache__/ +*.egg +*.egg-info # MODULE.bazel.lock is ignored for now as per recommendation from upstream. # See https://github.com/bazelbuild/bazel/issues/20369 diff --git a/CHANGELOG.md b/CHANGELOG.md index c9f812f3bd..2949697201 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ END_UNRELEASED_TEMPLATE {#v0-0-0-fixed} ### Fixed +* (pip) Made `config_settings` optional in `pip.default()` when not using platform-specific configurations. * (tests) No more coverage warnings are being printed if there are no sources. ([#2762](https://github.com/bazel-contrib/rules_python/issues/2762)) * (gazelle) Ancestor `conftest.py` files are added in addition to sibling `conftest.py`. @@ -71,6 +72,8 @@ END_UNRELEASED_TEMPLATE {#v0-0-0-added} ### Added +* (pip,python) Added `pyproject_toml` attribute to `pip.default()` and `python.defaults()` + to read Python version from pyproject.toml `requires-python` field (must be `==X.Y.Z` format). * (binaries/tests) {obj}`--debugger`: allows specifying an extra dependency to add to binaries/tests for custom debuggers. * (binaries/tests) Build information is now included in binaries and tests. diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel index efc1dd7319..c25a1885f1 100644 --- a/python/private/BUILD.bazel +++ b/python/private/BUILD.bazel @@ -29,7 +29,10 @@ package( licenses(["notice"]) -exports_files(["runtime_env_toolchain_interpreter.sh"]) +exports_files([ + "runtime_env_toolchain_interpreter.sh", + "pyproject_version_extractor.py", +]) filegroup( name = "distribution", @@ -269,6 +272,8 @@ bzl_library( deps = [ ":full_version_bzl", ":platform_info_bzl", + ":pyproject_repo_bzl", + ":pyproject_utils_bzl", ":python_register_toolchains_bzl", ":pythons_hub_bzl", ":repo_utils_bzl", @@ -746,6 +751,16 @@ bzl_library( ], ) +bzl_library( + name = "pyproject_repo_bzl", + srcs = ["pyproject_repo.bzl"], +) + +bzl_library( + name = "pyproject_utils_bzl", + srcs = ["pyproject_utils.bzl"], +) + # Needed to define bzl_library targets for docgen. (We don't define the # bzl_library target here because it'd give our users a transitive dependency # on Skylib.) diff --git a/python/private/pypi/BUILD.bazel b/python/private/pypi/BUILD.bazel index b46fd58d3c..7ed6fd89e3 100644 --- a/python/private/pypi/BUILD.bazel +++ b/python/private/pypi/BUILD.bazel @@ -126,6 +126,7 @@ bzl_library( ":whl_library_bzl", "//python/private:auth_bzl", "//python/private:normalize_name_bzl", + "//python/private:pyproject_utils_bzl", "//python/private:repo_utils_bzl", "@bazel_features//:features", "@pythons_hub//:interpreters_bzl", diff --git a/python/private/pypi/extension.bzl b/python/private/pypi/extension.bzl index 3927f61c00..60f07b52be 100644 --- a/python/private/pypi/extension.bzl +++ b/python/private/pypi/extension.bzl @@ -19,6 +19,7 @@ load("@pythons_hub//:versions.bzl", "MINOR_MAPPING") load("@rules_python_internal//:rules_python_config.bzl", rp_config = "config") load("//python/private:auth.bzl", "AUTH_ATTRS") load("//python/private:normalize_name.bzl", "normalize_name") +load("//python/private:pyproject_utils.bzl", "read_pyproject_version") load("//python/private:repo_utils.bzl", "repo_utils") load(":evaluate_markers.bzl", EVALUATE_MARKERS_SRCS = "SRCS") load(":hub_builder.bzl", "hub_builder") @@ -86,12 +87,23 @@ def build_config( """ defaults = { "platforms": {}, + "python_version": None, } for mod in module_ctx.modules: if not (mod.is_root or mod.name == "rules_python"): continue for tag in mod.tags.default: + pyproject_toml = getattr(tag, "pyproject_toml", None) + if pyproject_toml: + pyproject_version = read_pyproject_version( + module_ctx, + pyproject_toml, + logger = None, + ) + if pyproject_version: + defaults["python_version"] = pyproject_version + platform = tag.platform if platform: specific_config = defaults["platforms"].setdefault(platform, {}) @@ -125,6 +137,7 @@ def build_config( return struct( auth_patterns = defaults.get("auth_patterns", {}), netrc = defaults.get("netrc", None), + python_version = defaults.get("python_version", None), platforms = { name: _plat(**values) for name, values in defaults["platforms"].items() @@ -155,6 +168,8 @@ def parse_modules( Returns: A struct with the following attributes: """ + config = build_config(module_ctx = module_ctx, enable_pipstar = enable_pipstar, enable_pipstar_extract = enable_pipstar_extract) + whl_mods = {} for mod in module_ctx.modules: for whl_mod in mod.tags.whl_mods: @@ -186,8 +201,6 @@ You cannot use both the additive_build_content and additive_build_content_file a srcs_exclude_glob = whl_mod.srcs_exclude_glob, ) - config = build_config(module_ctx = module_ctx, enable_pipstar = enable_pipstar, enable_pipstar_extract = enable_pipstar_extract) - # TODO @aignas 2025-06-03: Merge override API with the builder? _overriden_whl_set = {} whl_overrides = {} @@ -226,6 +239,10 @@ You cannot use both the additive_build_content and additive_build_content_file a for mod in module_ctx.modules: for pip_attr in mod.tags.parse: + python_version = pip_attr.python_version or config.python_version + if not python_version: + _fail("pip.parse() requires either python_version attribute or pip.default(pyproject_toml=...) to be set") + hub_name = pip_attr.hub_name if hub_name not in pip_hub_map: builder = hub_builder( @@ -262,6 +279,7 @@ You cannot use both the additive_build_content and additive_build_content_file a builder.pip_parse( module_ctx, pip_attr = pip_attr, + python_version = python_version, ) # Keeps track of all the hub's whl repos across the different versions. @@ -407,7 +425,7 @@ Either this or {attr}`env` `platform_machine` key should be specified. """, ), "config_settings": attr.label_list( - mandatory = True, + mandatory = False, doc = """\ The list of labels to `config_setting` targets that need to be matched for the platform to be selected. @@ -469,6 +487,22 @@ If you are defining custom platforms in your project and don't want things to cl [isolation] feature. [isolation]: https://bazel.build/rules/lib/globals/module#use_extension.isolate +""", + ), + "pyproject_toml": attr.label( + mandatory = False, + allow_single_file = True, + doc = """\ +Label pointing to pyproject.toml file to read the default Python version from. +When specified, reads the `requires-python` field from pyproject.toml and uses +it as the default python_version for all `pip.parse()` calls that don't +explicitly specify one. + +The version must be specified as `==X.Y.Z` (exact version with full semver). +This is designed to work with dependency management tools like Renovate. + +:::{versionadded} 1.8.0 +::: """, ), "whl_abi_tags": attr.string_list( @@ -649,7 +683,7 @@ find in case extra indexes are specified. default = True, ), "python_version": attr.string( - mandatory = True, + mandatory = False, doc = """ The Python version the dependencies are targetting, in Major.Minor format (e.g., "3.11") or patch level granularity (e.g. "3.11.1"). diff --git a/python/private/pypi/hub_builder.bzl b/python/private/pypi/hub_builder.bzl index f54d02d8b0..fbb08d24b9 100644 --- a/python/private/pypi/hub_builder.bzl +++ b/python/private/pypi/hub_builder.bzl @@ -113,8 +113,8 @@ def _build(self): whl_libraries = self._whl_libraries, ) -def _pip_parse(self, module_ctx, pip_attr): - python_version = pip_attr.python_version +def _pip_parse(self, module_ctx, pip_attr, python_version = None): + python_version = python_version or pip_attr.python_version if python_version in self._platforms: fail(( "Duplicate pip python version '{version}' for hub " + @@ -163,8 +163,9 @@ def _pip_parse(self, module_ctx, pip_attr): self, module_ctx, pip_attr = pip_attr, - enable_pipstar = self._config.enable_pipstar or self._get_index_urls.get(pip_attr.python_version), - enable_pipstar_extract = self._config.enable_pipstar_extract or self._get_index_urls.get(pip_attr.python_version), + python_version = python_version, + enable_pipstar = self._config.enable_pipstar or self._get_index_urls.get(python_version), + enable_pipstar_extract = self._config.enable_pipstar_extract or self._get_index_urls.get(python_version), ) ### end of PUBLIC methods @@ -346,11 +347,11 @@ def _set_get_index_urls(self, pip_attr): ) return True -def _detect_interpreter(self, pip_attr): +def _detect_interpreter(self, pip_attr, python_version): python_interpreter_target = pip_attr.python_interpreter_target if python_interpreter_target == None and not pip_attr.python_interpreter: python_name = "python_{}_host".format( - pip_attr.python_version.replace(".", "_"), + python_version.replace(".", "_"), ) if python_name not in self._available_interpreters: fail(( @@ -360,7 +361,7 @@ def _detect_interpreter(self, pip_attr): "Expected to find {python_name} among registered versions:\n {labels}" ).format( hub_name = self.name, - version = pip_attr.python_version, + version = python_version, python_name = python_name, labels = " \n".join(self._available_interpreters), )) @@ -424,17 +425,17 @@ def _platforms(module_ctx, *, python_version, config, target_platforms): ) return platforms -def _evaluate_markers(self, pip_attr, enable_pipstar): +def _evaluate_markers(self, pip_attr, python_version, enable_pipstar): if self._evaluate_markers_fn: return self._evaluate_markers_fn if enable_pipstar: return lambda _, requirements: evaluate_markers_star( requirements = requirements, - platforms = self._platforms[pip_attr.python_version], + platforms = self._platforms[python_version], ) - interpreter = _detect_interpreter(self, pip_attr) + interpreter = _detect_interpreter(self, pip_attr, python_version) # NOTE @aignas 2024-08-02: , we will execute any interpreter that we find either # in the PATH or if specified as a label. We will configure the env @@ -454,7 +455,7 @@ def _evaluate_markers(self, pip_attr, enable_pipstar): module_ctx, requirements = { k: { - p: self._platforms[pip_attr.python_version][p].triple + p: self._platforms[python_version][p].triple for p in plats } for k, plats in requirements.items() @@ -470,6 +471,7 @@ def _create_whl_repos( module_ctx, *, pip_attr, + python_version, enable_pipstar = False, enable_pipstar_extract = False): """create all of the whl repositories @@ -478,11 +480,12 @@ def _create_whl_repos( self: the builder. module_ctx: {type}`module_ctx`. pip_attr: {type}`struct` - the struct that comes from the tag class iteration. + python_version: {type}`str` - the resolved python version for this pip.parse call. enable_pipstar: {type}`bool` - enable the pipstar or not. enable_pipstar_extract: {type}`bool` - enable the pipstar extraction or not. """ logger = self._logger - platforms = self._platforms[pip_attr.python_version] + platforms = self._platforms[python_version] requirements_by_platform = parse_requirements( module_ctx, requirements_by_platform = requirements_files_by_platform( @@ -494,15 +497,15 @@ def _create_whl_repos( extra_pip_args = pip_attr.extra_pip_args, platforms = sorted(platforms), # here we only need keys python_version = full_version( - version = pip_attr.python_version, + version = python_version, minor_mapping = self._minor_mapping, ), logger = logger, ), platforms = platforms, extra_pip_args = pip_attr.extra_pip_args, - get_index_urls = self._get_index_urls.get(pip_attr.python_version), - evaluate_markers = _evaluate_markers(self, pip_attr, enable_pipstar), + get_index_urls = self._get_index_urls.get(python_version), + evaluate_markers = _evaluate_markers(self, pip_attr, python_version, enable_pipstar), logger = logger, ) @@ -524,7 +527,7 @@ def _create_whl_repos( enable_pipstar = enable_pipstar, ) - interpreter = _detect_interpreter(self, pip_attr) + interpreter = _detect_interpreter(self, pip_attr, python_version) for whl in requirements_by_platform: whl_library_args = common_args | _whl_library_args( @@ -538,9 +541,9 @@ def _create_whl_repos( whl_library_args = whl_library_args, download_only = pip_attr.download_only, netrc = self._config.netrc or pip_attr.netrc, - use_downloader = _use_downloader(self, pip_attr.python_version, whl.name), + use_downloader = _use_downloader(self, python_version, whl.name), auth_patterns = self._config.auth_patterns or pip_attr.auth_patterns, - python_version = _major_minor_version(pip_attr.python_version), + python_version = _major_minor_version(python_version), is_multiple_versions = whl.is_multiple_versions, interpreter = interpreter, enable_pipstar = enable_pipstar, @@ -548,7 +551,7 @@ def _create_whl_repos( ) _add_whl_library( self, - python_version = pip_attr.python_version, + python_version = python_version, whl = whl, repo = repo, enable_pipstar = enable_pipstar, diff --git a/python/private/pyproject_repo.bzl b/python/private/pyproject_repo.bzl new file mode 100644 index 0000000000..e62ec2b4e4 --- /dev/null +++ b/python/private/pyproject_repo.bzl @@ -0,0 +1,64 @@ +"""Repository rule to expose Python version from pyproject.toml.""" + +_EXTRACTOR_SCRIPT = Label("//python/private:pyproject_version_extractor.py") + +def _pyproject_version_repo_impl(rctx): + """Create a repository that exports PYTHON_VERSION from pyproject.toml.""" + pyproject_path = rctx.path(rctx.attr.pyproject_toml) + rctx.read(pyproject_path, watch = "yes") + + # Use the shared extractor script + extractor = rctx.path(_EXTRACTOR_SCRIPT) + result = rctx.execute([ + "python3", + str(extractor), + str(pyproject_path), + ]) + + if result.return_code != 0: + fail("Failed to read Python version from pyproject.toml: " + result.stderr) + + version = result.stdout.strip() + + # Create a .bzl file that exports the version + rctx.file("version.bzl", """ +\"\"\"Python version from pyproject.toml. + +This file is automatically generated. Do not edit. +\"\"\" + +PYTHON_VERSION = "{version}" +""".format(version = version)) + + rctx.file("BUILD.bazel", """ +# Automatically generated from pyproject.toml +exports_files(["version.bzl"]) +""") + +pyproject_version_repo = repository_rule( + implementation = _pyproject_version_repo_impl, + attrs = { + "pyproject_toml": attr.label( + mandatory = True, + allow_single_file = True, + doc = "Label pointing to pyproject.toml file.", + ), + }, + doc = """Repository rule that reads Python version from pyproject.toml. + +This rule creates a repository with a `version.bzl` file that exports +`PYTHON_VERSION` constant. This allows BUILD files to import the Python +version without hardcoding it. + +Example: +```python + load("@python_version_from_pyproject//:version.bzl", "PYTHON_VERSION") + + compile_pip_requirements( + name = "requirements", + python_version = PYTHON_VERSION, + requirements_txt = "requirements.txt", + ) +``` +""", +) diff --git a/python/private/pyproject_utils.bzl b/python/private/pyproject_utils.bzl new file mode 100644 index 0000000000..eaf03332e2 --- /dev/null +++ b/python/private/pyproject_utils.bzl @@ -0,0 +1,38 @@ +"""Utilities for reading Python version from pyproject.toml.""" + +_EXTRACTOR_SCRIPT = Label("//python/private:pyproject_version_extractor.py") + +def read_pyproject_version(module_ctx, pyproject_label, logger = None): + """Reads Python version from pyproject.toml if requested. + + Args: + module_ctx: The module_ctx object from the module extension. + pyproject_label: Label pointing to the pyproject.toml file, or None. + logger: Optional logger instance for informational messages. + + Returns: + The Python version string (e.g. "3.13.9") or None if pyproject_label is None. + """ + if not pyproject_label: + return None + + pyproject_path = module_ctx.path(pyproject_label) + module_ctx.read(pyproject_path, watch = "yes") + + # Use the shared extractor script + extractor = module_ctx.path(_EXTRACTOR_SCRIPT) + result = module_ctx.execute([ + "python3", + str(extractor), + str(pyproject_path), + ]) + + if result.return_code != 0: + fail("Failed to read Python version from pyproject.toml: " + result.stderr) + + version = result.stdout.strip() + + if logger: + logger.info(lambda: "Read Python version {} from {}".format(version, pyproject_label)) + + return version diff --git a/python/private/pyproject_version_extractor.py b/python/private/pyproject_version_extractor.py new file mode 100644 index 0000000000..1cfdbd1fbc --- /dev/null +++ b/python/private/pyproject_version_extractor.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +"""Extract Python version from pyproject.toml.""" + +import re +import sys + +try: + import tomllib as toml +except ImportError: + try: + import tomli as toml + except ImportError: + raise SystemExit( + "need tomllib (python >=3.11) or tomli installed on host python" + ) + + +def validate_and_extract(pyproject_path): + """Validate format and extract version.""" + with open(pyproject_path, "rb") as f: + data = toml.load(f) + + version = data["project"]["requires-python"] + + if not version.startswith("=="): + sys.exit(f"requires-python must use '==' for exact version, got: {version}") + + bare_version = version[2:].strip() + + if not re.match(r"^\d+\.\d+\.\d+$", bare_version): + sys.exit(f"requires-python must be in X.Y.Z format, got: {bare_version}") + + return bare_version + + +def main(): + if len(sys.argv) < 2: + sys.exit("Usage: pyproject_version_extractor.py ") + + version = validate_and_extract(sys.argv[1]) + print(version) + + +if __name__ == "__main__": + main() diff --git a/python/private/python.bzl b/python/private/python.bzl index 399743c18d..8b226336e0 100644 --- a/python/private/python.bzl +++ b/python/private/python.bzl @@ -19,6 +19,8 @@ load("//python:versions.bzl", "DEFAULT_RELEASE_BASE_URL", "PLATFORMS", "TOOL_VER load(":auth.bzl", "AUTH_ATTRS") load(":full_version.bzl", "full_version") load(":platform_info.bzl", "platform_info") +load(":pyproject_repo.bzl", "pyproject_version_repo") +load(":pyproject_utils.bzl", "read_pyproject_version") load(":python_register_toolchains.bzl", "python_register_toolchains") load(":pythons_hub.bzl", "hub_repo") load(":repo_utils.bzl", "repo_utils") @@ -87,6 +89,8 @@ def parse_modules(*, module_ctx, logger, _fail = fail): mod = mod, seen_versions = seen_versions, config = config, + module_ctx = module_ctx, + logger = logger, ) for toolchain_attr in toolchain_attr_structs: @@ -216,6 +220,20 @@ def _python_impl(module_ctx): logger = repo_utils.logger(module_ctx, "python") py = parse_modules(module_ctx = module_ctx, logger = logger) + # Create pyproject version repo if pyproject.toml is used + created_pyproject_repo = False + for mod in module_ctx.modules: + if mod.is_root: + for tag in mod.tags.defaults: + if tag.pyproject_toml: + pyproject_version_repo( + name = "python_version_from_pyproject", + pyproject_toml = tag.pyproject_toml, + ) + created_pyproject_repo = True + break + break + # Host compatible runtime repos # dict[str version, struct] where struct has: # * full_python_version: str @@ -455,7 +473,16 @@ def _python_impl(module_ctx): ) if bazel_features.external_deps.extension_metadata_has_reproducible: - return module_ctx.extension_metadata(reproducible = True) + # Build the list of direct dependencies + root_direct_deps = ["pythons_hub", "python_versions"] + if created_pyproject_repo: + root_direct_deps.append("python_version_from_pyproject") + + return module_ctx.extension_metadata( + root_module_direct_deps = root_direct_deps, + root_module_direct_dev_deps = [], + reproducible = True, + ) else: return None @@ -852,8 +879,15 @@ def _compute_default_python_version(mctx): defaults_attr_structs = _create_defaults_attr_structs(mod = mod) default_python_version_env = None default_python_version_file = None + pyproject_toml_label = None for defaults_attr in defaults_attr_structs: + pyproject_toml_label = _one_or_the_same( + pyproject_toml_label, + defaults_attr.pyproject_toml, + onerror = lambda: fail("Multiple pyproject.toml files specified in defaults"), + ) + default_python_version = _one_or_the_same( default_python_version, defaults_attr.python_version, @@ -869,12 +903,22 @@ def _compute_default_python_version(mctx): defaults_attr.python_version_file, onerror = _fail_multiple_defaults_python_version_file, ) - if default_python_version_file: + + # Priority order: pyproject_toml > python_version_file > python_version_env > python_version + if pyproject_toml_label: + pyproject_version = read_pyproject_version( + mctx, + pyproject_toml_label, + logger = None, + ) + if pyproject_version: + default_python_version = pyproject_version + elif default_python_version_file: default_python_version = _one_or_the_same( default_python_version, mctx.read(default_python_version_file, watch = "yes").strip(), ) - if default_python_version_env: + elif default_python_version_env: default_python_version = mctx.getenv( default_python_version_env, default_python_version, @@ -915,11 +959,29 @@ def _create_defaults_attr_struct(*, tag): python_version = getattr(tag, "python_version", None), python_version_env = getattr(tag, "python_version_env", None), python_version_file = getattr(tag, "python_version_file", None), + pyproject_toml = getattr(tag, "pyproject_toml", None), ) -def _create_toolchain_attr_structs(*, mod, config, seen_versions): +def _create_toolchain_attr_structs(*, mod, config, seen_versions, module_ctx, logger): arg_structs = [] + # Check if pyproject_toml was specified in defaults + # If so, register a toolchain for it + for tag in mod.tags.defaults: + pyproject_toml = getattr(tag, "pyproject_toml", None) + if pyproject_toml: + pyproject_version = read_pyproject_version( + module_ctx, + pyproject_toml, + logger, + ) + if pyproject_version and pyproject_version not in seen_versions: + arg_structs.append(_create_toolchain_attrs_struct( + python_version = pyproject_version, + toolchain_tag_count = 1, + )) + seen_versions[pyproject_version] = True + for tag in mod.tags.toolchain: arg_structs.append(_create_toolchain_attrs_struct( tag = tag, @@ -959,6 +1021,18 @@ def _create_toolchain_attrs_struct( _defaults = tag_class( doc = """Tag class to specify the default Python version.""", attrs = { + "pyproject_toml": attr.label( + mandatory = False, + allow_single_file = True, + doc = """\ +Label pointing to pyproject.toml file to read the default Python version from. +When specified, reads the `requires-python` field from pyproject.toml. +The version must be specified as `==X.Y.Z` (exact version with full semver). + +:::{versionadded} 1.8.0 +::: +""", + ), "python_version": attr.string( mandatory = False, doc = """\ diff --git a/tests/pyproject/BUILD.bazel b/tests/pyproject/BUILD.bazel new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/pyproject/compile_requirements_test/BUILD.bazel b/tests/pyproject/compile_requirements_test/BUILD.bazel new file mode 100644 index 0000000000..970bcd287f --- /dev/null +++ b/tests/pyproject/compile_requirements_test/BUILD.bazel @@ -0,0 +1,14 @@ +load("@python_version_from_pyproject//:version.bzl", "PYTHON_VERSION") +load("@rules_python//python:defs.bzl", "py_test") +load("@rules_python//python:pip.bzl", "compile_pip_requirements") + +compile_pip_requirements( + name = "requirements", + python_version = PYTHON_VERSION, + requirements_txt = "requirements_lock.txt", +) + +py_test( + name = "test_compile_requirements", + srcs = ["test_compile_requirements.py"], +) diff --git a/tests/pyproject/compile_requirements_test/MODULE.bazel b/tests/pyproject/compile_requirements_test/MODULE.bazel new file mode 100644 index 0000000000..bccb07e00e --- /dev/null +++ b/tests/pyproject/compile_requirements_test/MODULE.bazel @@ -0,0 +1,13 @@ +"""Test that pyproject.toml version can be used in compile_pip_requirements.""" + +module(name = "compile_requirements_test") + +bazel_dep(name = "rules_python", version = "") +local_path_override( + module_name = "rules_python", + path = "../../..", +) + +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python.defaults(pyproject_toml = "//:pyproject.toml") +use_repo(python, "python_version_from_pyproject", "python_versions", "pythons_hub") diff --git a/tests/pyproject/compile_requirements_test/pyproject.toml b/tests/pyproject/compile_requirements_test/pyproject.toml new file mode 100644 index 0000000000..6640278aae --- /dev/null +++ b/tests/pyproject/compile_requirements_test/pyproject.toml @@ -0,0 +1,8 @@ +[project] +name = "compile-requirements-test" +dynamic = ["version"] +requires-python = "==3.11.7" + +dependencies = [ + "requests==2.32.5" +] diff --git a/tests/pyproject/compile_requirements_test/requirements_lock.txt b/tests/pyproject/compile_requirements_test/requirements_lock.txt new file mode 100644 index 0000000000..65cbf853f4 --- /dev/null +++ b/tests/pyproject/compile_requirements_test/requirements_lock.txt @@ -0,0 +1,137 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# bazel run //:requirements.update +# +certifi==2026.1.4 \ + --hash=sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c \ + --hash=sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120 + # via requests +charset-normalizer==3.4.4 \ + --hash=sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad \ + --hash=sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93 \ + --hash=sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394 \ + --hash=sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89 \ + --hash=sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc \ + --hash=sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86 \ + --hash=sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63 \ + --hash=sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d \ + --hash=sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f \ + --hash=sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8 \ + --hash=sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0 \ + --hash=sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505 \ + --hash=sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161 \ + --hash=sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af \ + --hash=sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152 \ + --hash=sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318 \ + --hash=sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72 \ + --hash=sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4 \ + --hash=sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e \ + --hash=sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3 \ + --hash=sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576 \ + --hash=sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c \ + --hash=sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1 \ + --hash=sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8 \ + --hash=sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1 \ + --hash=sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2 \ + --hash=sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44 \ + --hash=sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26 \ + --hash=sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88 \ + --hash=sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016 \ + --hash=sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede \ + --hash=sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf \ + --hash=sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a \ + --hash=sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc \ + --hash=sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0 \ + --hash=sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84 \ + --hash=sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db \ + --hash=sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1 \ + --hash=sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7 \ + --hash=sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed \ + --hash=sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8 \ + --hash=sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133 \ + --hash=sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e \ + --hash=sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef \ + --hash=sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14 \ + --hash=sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2 \ + --hash=sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0 \ + --hash=sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d \ + --hash=sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828 \ + --hash=sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f \ + --hash=sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf \ + --hash=sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6 \ + --hash=sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328 \ + --hash=sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090 \ + --hash=sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa \ + --hash=sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381 \ + --hash=sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c \ + --hash=sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb \ + --hash=sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc \ + --hash=sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a \ + --hash=sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec \ + --hash=sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc \ + --hash=sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac \ + --hash=sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e \ + --hash=sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313 \ + --hash=sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569 \ + --hash=sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3 \ + --hash=sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d \ + --hash=sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525 \ + --hash=sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894 \ + --hash=sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3 \ + --hash=sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9 \ + --hash=sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a \ + --hash=sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9 \ + --hash=sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14 \ + --hash=sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25 \ + --hash=sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50 \ + --hash=sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf \ + --hash=sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1 \ + --hash=sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3 \ + --hash=sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac \ + --hash=sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e \ + --hash=sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815 \ + --hash=sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c \ + --hash=sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6 \ + --hash=sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6 \ + --hash=sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e \ + --hash=sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4 \ + --hash=sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84 \ + --hash=sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69 \ + --hash=sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15 \ + --hash=sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191 \ + --hash=sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0 \ + --hash=sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897 \ + --hash=sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd \ + --hash=sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2 \ + --hash=sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794 \ + --hash=sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d \ + --hash=sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074 \ + --hash=sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3 \ + --hash=sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224 \ + --hash=sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838 \ + --hash=sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a \ + --hash=sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d \ + --hash=sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d \ + --hash=sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f \ + --hash=sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8 \ + --hash=sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490 \ + --hash=sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966 \ + --hash=sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9 \ + --hash=sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3 \ + --hash=sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e \ + --hash=sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608 + # via requests +idna==3.11 \ + --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ + --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 + # via requests +requests==2.32.5 \ + --hash=sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6 \ + --hash=sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf + # via compile-requirements-test (pyproject.toml) +urllib3==2.6.3 \ + --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ + --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4 + # via requests diff --git a/tests/pyproject/compile_requirements_test/test_compile_requirements.py b/tests/pyproject/compile_requirements_test/test_compile_requirements.py new file mode 100644 index 0000000000..4a7924502c --- /dev/null +++ b/tests/pyproject/compile_requirements_test/test_compile_requirements.py @@ -0,0 +1,27 @@ +"""Test that compile_pip_requirements works with PYTHON_VERSION from pyproject.toml.""" + +import sys +import unittest + + +class CompileRequirementsTest(unittest.TestCase): + def test_python_version_matches_pyproject(self): + """Verify we're using the Python version from pyproject.toml.""" + version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}" + self.assertEqual( + version, + "3.11.7", + f"Expected Python 3.11.7 from pyproject.toml, got {version}", + ) + + def test_requirements_target_exists(self): + """ + This test just needs to run successfully. + The fact that it runs means compile_pip_requirements worked, + which means PYTHON_VERSION was successfully loaded from the repo. + """ + self.assertTrue(True) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/pyproject/pip_integration_test/BUILD.bazel b/tests/pyproject/pip_integration_test/BUILD.bazel new file mode 100644 index 0000000000..215e0fb910 --- /dev/null +++ b/tests/pyproject/pip_integration_test/BUILD.bazel @@ -0,0 +1,9 @@ +load("@rules_python//python:defs.bzl", "py_test") + +py_test( + name = "test_pip_works", + srcs = ["test_pip_works.py"], + deps = [ + "@pip//requests", + ], +) diff --git a/tests/pyproject/pip_integration_test/MODULE.bazel b/tests/pyproject/pip_integration_test/MODULE.bazel new file mode 100644 index 0000000000..c39ba1d18a --- /dev/null +++ b/tests/pyproject/pip_integration_test/MODULE.bazel @@ -0,0 +1,21 @@ +"""Test that pyproject.toml sets pip python_version.""" + +module(name = "pip_integration_test") + +bazel_dep(name = "rules_python", version = "") +local_path_override( + module_name = "rules_python", + path = "../../..", +) + +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python.defaults(pyproject_toml = "//:pyproject.toml") +use_repo(python, "python_version_from_pyproject", "python_versions", "pythons_hub") + +pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") +pip.default(pyproject_toml = "//:pyproject.toml") +pip.parse( + hub_name = "pip", + requirements_lock = "//:requirements_lock.txt", +) +use_repo(pip, "pip") diff --git a/tests/pyproject/pip_integration_test/pyproject.toml b/tests/pyproject/pip_integration_test/pyproject.toml new file mode 100644 index 0000000000..543898e95f --- /dev/null +++ b/tests/pyproject/pip_integration_test/pyproject.toml @@ -0,0 +1,8 @@ +[project] +name = "pip-integration-test" +dynamic = ["version"] +requires-python = "==3.11.7" + +dependencies = [ + "requests==2.32.5" +] diff --git a/tests/pyproject/pip_integration_test/requirements_lock.txt b/tests/pyproject/pip_integration_test/requirements_lock.txt new file mode 100644 index 0000000000..fa0204bbea --- /dev/null +++ b/tests/pyproject/pip_integration_test/requirements_lock.txt @@ -0,0 +1,137 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --generate-hashes --output-file=requirements_lock.txt pyproject.toml +# +certifi==2026.1.4 \ + --hash=sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c \ + --hash=sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120 + # via requests +charset-normalizer==3.4.4 \ + --hash=sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad \ + --hash=sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93 \ + --hash=sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394 \ + --hash=sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89 \ + --hash=sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc \ + --hash=sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86 \ + --hash=sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63 \ + --hash=sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d \ + --hash=sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f \ + --hash=sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8 \ + --hash=sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0 \ + --hash=sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505 \ + --hash=sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161 \ + --hash=sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af \ + --hash=sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152 \ + --hash=sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318 \ + --hash=sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72 \ + --hash=sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4 \ + --hash=sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e \ + --hash=sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3 \ + --hash=sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576 \ + --hash=sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c \ + --hash=sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1 \ + --hash=sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8 \ + --hash=sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1 \ + --hash=sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2 \ + --hash=sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44 \ + --hash=sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26 \ + --hash=sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88 \ + --hash=sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016 \ + --hash=sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede \ + --hash=sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf \ + --hash=sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a \ + --hash=sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc \ + --hash=sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0 \ + --hash=sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84 \ + --hash=sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db \ + --hash=sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1 \ + --hash=sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7 \ + --hash=sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed \ + --hash=sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8 \ + --hash=sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133 \ + --hash=sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e \ + --hash=sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef \ + --hash=sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14 \ + --hash=sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2 \ + --hash=sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0 \ + --hash=sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d \ + --hash=sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828 \ + --hash=sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f \ + --hash=sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf \ + --hash=sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6 \ + --hash=sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328 \ + --hash=sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090 \ + --hash=sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa \ + --hash=sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381 \ + --hash=sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c \ + --hash=sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb \ + --hash=sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc \ + --hash=sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a \ + --hash=sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec \ + --hash=sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc \ + --hash=sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac \ + --hash=sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e \ + --hash=sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313 \ + --hash=sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569 \ + --hash=sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3 \ + --hash=sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d \ + --hash=sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525 \ + --hash=sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894 \ + --hash=sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3 \ + --hash=sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9 \ + --hash=sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a \ + --hash=sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9 \ + --hash=sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14 \ + --hash=sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25 \ + --hash=sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50 \ + --hash=sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf \ + --hash=sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1 \ + --hash=sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3 \ + --hash=sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac \ + --hash=sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e \ + --hash=sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815 \ + --hash=sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c \ + --hash=sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6 \ + --hash=sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6 \ + --hash=sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e \ + --hash=sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4 \ + --hash=sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84 \ + --hash=sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69 \ + --hash=sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15 \ + --hash=sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191 \ + --hash=sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0 \ + --hash=sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897 \ + --hash=sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd \ + --hash=sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2 \ + --hash=sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794 \ + --hash=sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d \ + --hash=sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074 \ + --hash=sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3 \ + --hash=sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224 \ + --hash=sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838 \ + --hash=sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a \ + --hash=sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d \ + --hash=sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d \ + --hash=sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f \ + --hash=sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8 \ + --hash=sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490 \ + --hash=sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966 \ + --hash=sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9 \ + --hash=sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3 \ + --hash=sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e \ + --hash=sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608 + # via requests +idna==3.11 \ + --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ + --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 + # via requests +requests==2.32.5 \ + --hash=sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6 \ + --hash=sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf + # via compile-requirements-test (pyproject.toml) +urllib3==2.6.3 \ + --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \ + --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4 + # via requests diff --git a/tests/pyproject/pip_integration_test/test_pip_works.py b/tests/pyproject/pip_integration_test/test_pip_works.py new file mode 100644 index 0000000000..63d55c87ca --- /dev/null +++ b/tests/pyproject/pip_integration_test/test_pip_works.py @@ -0,0 +1,21 @@ +"""Test that pip dependencies resolve correctly with pyproject.toml.""" + +import sys +import unittest + + +class PipIntegrationTest(unittest.TestCase): + def test_python_version(self): + """Verify Python version from pyproject.toml.""" + version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}" + self.assertEqual(version, "3.11.7") + + def test_can_import_dependency(self): + """Verify pip dependency was resolved.""" + import requests + + self.assertTrue(callable(requests.get)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/pyproject/priority_test/.python-version b/tests/pyproject/priority_test/.python-version new file mode 100644 index 0000000000..655ff07381 --- /dev/null +++ b/tests/pyproject/priority_test/.python-version @@ -0,0 +1 @@ +3.13.9 diff --git a/tests/pyproject/priority_test/BUILD.bazel b/tests/pyproject/priority_test/BUILD.bazel new file mode 100644 index 0000000000..56685c1686 --- /dev/null +++ b/tests/pyproject/priority_test/BUILD.bazel @@ -0,0 +1,6 @@ +load("@rules_python//python:defs.bzl", "py_test") + +py_test( + name = "test_priority", + srcs = ["test_priority.py"], +) diff --git a/tests/pyproject/priority_test/MODULE.bazel b/tests/pyproject/priority_test/MODULE.bazel new file mode 100644 index 0000000000..8754c77f37 --- /dev/null +++ b/tests/pyproject/priority_test/MODULE.bazel @@ -0,0 +1,16 @@ +"""Test that pyproject.toml takes priority over other version sources.""" + +module(name = "priority_test") + +bazel_dep(name = "rules_python", version = "") +local_path_override( + module_name = "rules_python", + path = "../../..", +) + +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python.defaults( + pyproject_toml = "//:pyproject.toml", + python_version_file = "//:.python-version", +) +use_repo(python, "python_version_from_pyproject", "python_versions", "pythons_hub") diff --git a/tests/pyproject/priority_test/pyproject.toml b/tests/pyproject/priority_test/pyproject.toml new file mode 100644 index 0000000000..8268f6f5d7 --- /dev/null +++ b/tests/pyproject/priority_test/pyproject.toml @@ -0,0 +1,4 @@ +[project] +name = "priority-test" +dynamic = ["version"] +requires-python = "==3.11.7" diff --git a/tests/pyproject/priority_test/test_priority.py b/tests/pyproject/priority_test/test_priority.py new file mode 100644 index 0000000000..2dff7e858c --- /dev/null +++ b/tests/pyproject/priority_test/test_priority.py @@ -0,0 +1,21 @@ +"""Test that pyproject.toml takes priority over .python-version.""" + +import sys +import unittest + + +class PriorityTest(unittest.TestCase): + def test_pyproject_wins_over_python_version_file(self): + """ + Verify pyproject.toml (3.11.7) takes priority over .python-version (3.12.0). + """ + version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}" + self.assertEqual( + version, + "3.11.7", + "pyproject.toml should take priority over .python-version file", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/pyproject/python_toolchain_test/BUILD.bazel b/tests/pyproject/python_toolchain_test/BUILD.bazel new file mode 100644 index 0000000000..5dd2afcbf3 --- /dev/null +++ b/tests/pyproject/python_toolchain_test/BUILD.bazel @@ -0,0 +1,7 @@ +load("@rules_python//python:defs.bzl", "py_test") + +py_test( + name = "test_version", + srcs = ["test_version.py"], + main = "test_version.py", +) diff --git a/tests/pyproject/python_toolchain_test/MODULE.bazel b/tests/pyproject/python_toolchain_test/MODULE.bazel new file mode 100644 index 0000000000..bcf9fc2069 --- /dev/null +++ b/tests/pyproject/python_toolchain_test/MODULE.bazel @@ -0,0 +1,13 @@ +"""Test that pyproject.toml sets Python toolchain version.""" + +module(name = "python_toolchain_test") + +bazel_dep(name = "rules_python", version = "") +local_path_override( + module_name = "rules_python", + path = "../../..", +) + +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python.defaults(pyproject_toml = "//:pyproject.toml") +use_repo(python, "python_version_from_pyproject", "python_versions", "pythons_hub") diff --git a/tests/pyproject/python_toolchain_test/pyproject.toml b/tests/pyproject/python_toolchain_test/pyproject.toml new file mode 100644 index 0000000000..aea4119799 --- /dev/null +++ b/tests/pyproject/python_toolchain_test/pyproject.toml @@ -0,0 +1,4 @@ +[project] +name = "python-toolchain-test" +dynamic = ["version"] +requires-python = "==3.11.7" diff --git a/tests/pyproject/python_toolchain_test/test_version.py b/tests/pyproject/python_toolchain_test/test_version.py new file mode 100644 index 0000000000..09ef166f6c --- /dev/null +++ b/tests/pyproject/python_toolchain_test/test_version.py @@ -0,0 +1,19 @@ +"""Test that Python version matches pyproject.toml requires-python.""" + +import sys +import unittest + + +class PythonVersionTest(unittest.TestCase): + def test_python_version_from_pyproject(self): + """Verify we're running Python 3.11.7 as specified in pyproject.toml.""" + version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}" + self.assertEqual( + version, + "3.11.7", + f"Expected Python 3.11.7 from pyproject.toml, got {version}", + ) + + +if __name__ == "__main__": + unittest.main()