refactor!: do not use a wrapper macro for pip_parse (#1514)
This brings back the generated documentation for the pip_parse
attributes making switches to default values more prominent.
Towards #1496.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4c1c0b7..2f593b0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,11 +29,16 @@
`GAZELLE_PYTHON_RUNTIME_DEPS` from `@rules_python_gazelle_plugin//:def.bzl` is
no longer necessary.
+* The installation of `pip_parse` repository rule toolchain dependencies is now
+ done as part of `py_repositories` call.
+
Breaking changes:
* (pip) `pip_install` repository rule in this release has been disabled and
will fail by default. The API symbol is going to be removed in the next
- version, please migrate to `pip_parse` as a replacement.
+ version, please migrate to `pip_parse` as a replacement. The `pip_parse`
+ rule no longer supports `requirements` attribute, please use
+ `requirements_lock` instead.
* (py_wheel) switch `incompatible_normalize_name` and
`incompatible_normalize_version` to `True` by default to enforce `PEP440`
diff --git a/python/pip.bzl b/python/pip.bzl
index 0d206e8..b779f83 100644
--- a/python/pip.bzl
+++ b/python/pip.bzl
@@ -20,7 +20,6 @@
"""
load("//python/pip_install:pip_repository.bzl", "pip_repository", _package_annotation = "package_annotation")
-load("//python/pip_install:repositories.bzl", "pip_install_dependencies")
load("//python/pip_install:requirements.bzl", _compile_pip_requirements = "compile_pip_requirements")
load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED")
load("//python/private:full_version.bzl", "full_version")
@@ -28,6 +27,7 @@
compile_pip_requirements = _compile_pip_requirements
package_annotation = _package_annotation
+pip_parse = pip_repository
def pip_install(requirements = None, name = "pip", allow_pip_install = False, **kwargs):
"""Will be removed in 0.28.0
@@ -44,41 +44,6 @@
else:
fail("pip_install support has been disabled, please use pip_parse as a replacement.")
-def pip_parse(requirements = None, requirements_lock = None, name = "pip_parsed_deps", **kwargs):
- """Accepts a locked/compiled requirements file and installs the dependencies listed within.
-
- Those dependencies become available as addressable targets and
- in a generated `requirements.bzl` file. The `requirements.bzl` file can
- be checked into source control, if desired; see {ref}`vendoring-requirements`
-
- For more information, see {ref}`pip-integration`.
-
- Args:
- requirements_lock (Label): A fully resolved 'requirements.txt' pip requirement file
- containing the transitive set of your dependencies. If this file is passed instead
- of 'requirements' no resolve will take place and pip_repository will create
- individual repositories for each of your dependencies so that wheels are
- fetched/built only for the targets specified by 'build/run/test'.
- Note that if your lockfile is platform-dependent, you can use the `requirements_[platform]`
- attributes.
- requirements (Label): Deprecated. See requirements_lock.
- name (str, optional): The name of the generated repository. The generated repositories
- containing each requirement will be of the form `<name>_<requirement-name>`.
- **kwargs (dict): Additional arguments to the [`pip_repository`](./pip_repository.md) repository rule.
- """
- pip_install_dependencies()
-
- # Temporary compatibility shim.
- # pip_install was previously document to use requirements while pip_parse was using requirements_lock.
- # We would prefer everyone move to using requirements_lock, but we maintain a temporary shim.
- reqs_to_use = requirements_lock if requirements_lock else requirements
-
- pip_repository(
- name = name,
- requirements_lock = reqs_to_use,
- **kwargs
- )
-
def _multi_pip_parse_impl(rctx):
rules_python = rctx.attr._rules_python_workspace.workspace_name
load_statements = []
diff --git a/python/pip_install/pip_repository.bzl b/python/pip_install/pip_repository.bzl
index f9d3676..36a777b 100644
--- a/python/pip_install/pip_repository.bzl
+++ b/python/pip_install/pip_repository.bzl
@@ -454,10 +454,14 @@
),
"requirements_lock": attr.label(
allow_single_file = True,
- doc = """
-A fully resolved 'requirements.txt' pip requirement file containing the transitive set of your dependencies. If this file is passed instead
-of 'requirements' no resolve will take place and pip_repository will create individual repositories for each of your dependencies so that
-wheels are fetched/built only for the targets specified by 'build/run/test'.
+ doc = """\
+A fully resolved 'requirements.txt' pip requirement file containing the
+transitive set of your dependencies. If this file is passed instead of
+'requirements' no resolve will take place and pip_repository will create
+individual repositories for each of your dependencies so that wheels are
+fetched/built only for the targets specified by 'build/run/test'. Note that if
+your lockfile is platform-dependent, you can use the `requirements_[platform]`
+attributes.
""",
),
"requirements_windows": attr.label(
@@ -473,22 +477,32 @@
pip_repository = repository_rule(
attrs = pip_repository_attrs,
- doc = """A rule for importing `requirements.txt` dependencies into Bazel.
+ doc = """Accepts a locked/compiled requirements file and installs the dependencies listed within.
-This rule imports a `requirements.txt` file and generates a new
-`requirements.bzl` file. This is used via the `WORKSPACE` pattern:
+Those dependencies become available in a generated `requirements.bzl` file.
+You can instead check this `requirements.bzl` file into your repo, see the "vendoring" section below.
-```python
-pip_repository(
- name = "foo",
- requirements = ":requirements.txt",
+This macro wraps the [`pip_repository`](./pip_repository.md) rule that invokes `pip`.
+In your WORKSPACE file:
+
+```starlark
+load("@rules_python//python:pip.bzl", "pip_parse")
+
+pip_parse(
+ name = "pip_deps",
+ requirements_lock = ":requirements.txt",
)
+
+load("@pip_deps//:requirements.bzl", "install_deps")
+
+install_deps()
```
-You can then reference imported dependencies from your `BUILD` file with:
+You can then reference installed dependencies from a `BUILD` file with:
-```python
-load("@foo//:requirements.bzl", "requirement")
+```starlark
+load("@pip_deps//:requirements.bzl", "requirement")
+
py_library(
name = "bar",
...
@@ -500,17 +514,52 @@
)
```
-Or alternatively:
-```python
-load("@foo//:requirements.bzl", "all_requirements")
-py_binary(
- name = "baz",
- ...
- deps = [
- ":foo",
- ] + all_requirements,
+In addition to the `requirement` macro, which is used to access the generated `py_library`
+target generated from a package's wheel, The generated `requirements.bzl` file contains
+functionality for exposing [entry points][whl_ep] as `py_binary` targets as well.
+
+[whl_ep]: https://packaging.python.org/specifications/entry-points/
+
+```starlark
+load("@pip_deps//:requirements.bzl", "entry_point")
+
+alias(
+ name = "pip-compile",
+ actual = entry_point(
+ pkg = "pip-tools",
+ script = "pip-compile",
+ ),
)
```
+
+Note that for packages whose name and script are the same, only the name of the package
+is needed when calling the `entry_point` macro.
+
+```starlark
+load("@pip_deps//:requirements.bzl", "entry_point")
+
+alias(
+ name = "flake8",
+ actual = entry_point("flake8"),
+)
+```
+
+## Vendoring the requirements.bzl file
+
+In some cases you may not want to generate the requirements.bzl file as a repository rule
+while Bazel is fetching dependencies. For example, if you produce a reusable Bazel module
+such as a ruleset, you may want to include the requirements.bzl file rather than make your users
+install the WORKSPACE setup to generate it.
+See https://github.com/bazelbuild/rules_python/issues/608
+
+This is the same workflow as Gazelle, which creates `go_repository` rules with
+[`update-repos`](https://github.com/bazelbuild/bazel-gazelle#update-repos)
+
+To do this, use the "write to source file" pattern documented in
+https://blog.aspect.dev/bazel-can-write-to-the-source-folder
+to put a copy of the generated requirements.bzl into your project.
+Then load the requirements.bzl file directly rather than from the generated repository.
+See the example in rules_python/examples/pip_parse_vendored.
""",
implementation = _pip_repository_impl,
environ = common_env,
diff --git a/python/pip_install/repositories.bzl b/python/pip_install/repositories.bzl
index b322a70..37500a6 100644
--- a/python/pip_install/repositories.bzl
+++ b/python/pip_install/repositories.bzl
@@ -14,10 +14,8 @@
""
-load("@bazel_skylib//lib:versions.bzl", "versions")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
-load("//:version.bzl", "MINIMUM_BAZEL_VERSION")
_RULE_DEPS = [
# START: maintained by 'bazel run //tools/private:update_pip_deps'
@@ -137,13 +135,6 @@
(However we call it from pip_install, making it optional for users to do so.)
"""
-
- # We only support Bazel LTS and rolling releases.
- # Give the user an obvious error to upgrade rather than some obscure missing symbol later.
- # It's not guaranteed that users call this function, but it's used by all the pip fetch
- # repository rules so it's likely that most users get the right error.
- versions.check(MINIMUM_BAZEL_VERSION)
-
for (name, url, sha256) in _RULE_DEPS:
maybe(
http_archive,
diff --git a/python/repositories.bzl b/python/repositories.bzl
index 5333c2d..498c80f 100644
--- a/python/repositories.bzl
+++ b/python/repositories.bzl
@@ -19,6 +19,7 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", _http_archive = "http_archive")
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe", "read_netrc", "read_user_netrc", "use_netrc")
+load("//python/pip_install:repositories.bzl", "pip_install_dependencies")
load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED")
load("//python/private:coverage_deps.bzl", "coverage_dep")
load("//python/private:full_version.bzl", "full_version")
@@ -59,6 +60,7 @@
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
],
)
+ pip_install_dependencies()
########
# Remaining content of the file is only used to support toolchains.