Skip to content

Commit a6ebc3c

Browse files
authored
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.
1 parent 4f7e6cd commit a6ebc3c

File tree

5 files changed

+81
-69
lines changed

5 files changed

+81
-69
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,16 @@ A brief description of the categories of changes:
2929
`GAZELLE_PYTHON_RUNTIME_DEPS` from `@rules_python_gazelle_plugin//:def.bzl` is
3030
no longer necessary.
3131

32+
* The installation of `pip_parse` repository rule toolchain dependencies is now
33+
done as part of `py_repositories` call.
34+
3235
Breaking changes:
3336

3437
* (pip) `pip_install` repository rule in this release has been disabled and
3538
will fail by default. The API symbol is going to be removed in the next
36-
version, please migrate to `pip_parse` as a replacement.
39+
version, please migrate to `pip_parse` as a replacement. The `pip_parse`
40+
rule no longer supports `requirements` attribute, please use
41+
`requirements_lock` instead.
3742

3843
* (py_wheel) switch `incompatible_normalize_name` and
3944
`incompatible_normalize_version` to `True` by default to enforce `PEP440`

python/pip.bzl

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ for internal use only.
2020
"""
2121

2222
load("//python/pip_install:pip_repository.bzl", "pip_repository", _package_annotation = "package_annotation")
23-
load("//python/pip_install:repositories.bzl", "pip_install_dependencies")
2423
load("//python/pip_install:requirements.bzl", _compile_pip_requirements = "compile_pip_requirements")
2524
load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED")
2625
load("//python/private:full_version.bzl", "full_version")
2726
load("//python/private:render_pkg_aliases.bzl", "NO_MATCH_ERROR_MESSAGE_TEMPLATE")
2827

2928
compile_pip_requirements = _compile_pip_requirements
3029
package_annotation = _package_annotation
30+
pip_parse = pip_repository
3131

3232
def pip_install(requirements = None, name = "pip", allow_pip_install = False, **kwargs):
3333
"""Will be removed in 0.28.0
@@ -44,41 +44,6 @@ def pip_install(requirements = None, name = "pip", allow_pip_install = False, **
4444
else:
4545
fail("pip_install support has been disabled, please use pip_parse as a replacement.")
4646

47-
def pip_parse(requirements = None, requirements_lock = None, name = "pip_parsed_deps", **kwargs):
48-
"""Accepts a locked/compiled requirements file and installs the dependencies listed within.
49-
50-
Those dependencies become available as addressable targets and
51-
in a generated `requirements.bzl` file. The `requirements.bzl` file can
52-
be checked into source control, if desired; see {ref}`vendoring-requirements`
53-
54-
For more information, see {ref}`pip-integration`.
55-
56-
Args:
57-
requirements_lock (Label): A fully resolved 'requirements.txt' pip requirement file
58-
containing the transitive set of your dependencies. If this file is passed instead
59-
of 'requirements' no resolve will take place and pip_repository will create
60-
individual repositories for each of your dependencies so that wheels are
61-
fetched/built only for the targets specified by 'build/run/test'.
62-
Note that if your lockfile is platform-dependent, you can use the `requirements_[platform]`
63-
attributes.
64-
requirements (Label): Deprecated. See requirements_lock.
65-
name (str, optional): The name of the generated repository. The generated repositories
66-
containing each requirement will be of the form `<name>_<requirement-name>`.
67-
**kwargs (dict): Additional arguments to the [`pip_repository`](./pip_repository.md) repository rule.
68-
"""
69-
pip_install_dependencies()
70-
71-
# Temporary compatibility shim.
72-
# pip_install was previously document to use requirements while pip_parse was using requirements_lock.
73-
# We would prefer everyone move to using requirements_lock, but we maintain a temporary shim.
74-
reqs_to_use = requirements_lock if requirements_lock else requirements
75-
76-
pip_repository(
77-
name = name,
78-
requirements_lock = reqs_to_use,
79-
**kwargs
80-
)
81-
8247
def _multi_pip_parse_impl(rctx):
8348
rules_python = rctx.attr._rules_python_workspace.workspace_name
8449
load_statements = []

python/pip_install/pip_repository.bzl

Lines changed: 72 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -454,10 +454,14 @@ pip_repository_attrs = {
454454
),
455455
"requirements_lock": attr.label(
456456
allow_single_file = True,
457-
doc = """
458-
A fully resolved 'requirements.txt' pip requirement file containing the transitive set of your dependencies. If this file is passed instead
459-
of 'requirements' no resolve will take place and pip_repository will create individual repositories for each of your dependencies so that
460-
wheels are fetched/built only for the targets specified by 'build/run/test'.
457+
doc = """\
458+
A fully resolved 'requirements.txt' pip requirement file containing the
459+
transitive set of your dependencies. If this file is passed instead of
460+
'requirements' no resolve will take place and pip_repository will create
461+
individual repositories for each of your dependencies so that wheels are
462+
fetched/built only for the targets specified by 'build/run/test'. Note that if
463+
your lockfile is platform-dependent, you can use the `requirements_[platform]`
464+
attributes.
461465
""",
462466
),
463467
"requirements_windows": attr.label(
@@ -473,22 +477,32 @@ pip_repository_attrs.update(**common_attrs)
473477

474478
pip_repository = repository_rule(
475479
attrs = pip_repository_attrs,
476-
doc = """A rule for importing `requirements.txt` dependencies into Bazel.
480+
doc = """Accepts a locked/compiled requirements file and installs the dependencies listed within.
481+
482+
Those dependencies become available in a generated `requirements.bzl` file.
483+
You can instead check this `requirements.bzl` file into your repo, see the "vendoring" section below.
477484
478-
This rule imports a `requirements.txt` file and generates a new
479-
`requirements.bzl` file. This is used via the `WORKSPACE` pattern:
485+
This macro wraps the [`pip_repository`](./pip_repository.md) rule that invokes `pip`.
486+
In your WORKSPACE file:
480487
481-
```python
482-
pip_repository(
483-
name = "foo",
484-
requirements = ":requirements.txt",
488+
```starlark
489+
load("@rules_python//python:pip.bzl", "pip_parse")
490+
491+
pip_parse(
492+
name = "pip_deps",
493+
requirements_lock = ":requirements.txt",
485494
)
495+
496+
load("@pip_deps//:requirements.bzl", "install_deps")
497+
498+
install_deps()
486499
```
487500
488-
You can then reference imported dependencies from your `BUILD` file with:
501+
You can then reference installed dependencies from a `BUILD` file with:
502+
503+
```starlark
504+
load("@pip_deps//:requirements.bzl", "requirement")
489505
490-
```python
491-
load("@foo//:requirements.bzl", "requirement")
492506
py_library(
493507
name = "bar",
494508
...
@@ -500,17 +514,52 @@ py_library(
500514
)
501515
```
502516
503-
Or alternatively:
504-
```python
505-
load("@foo//:requirements.bzl", "all_requirements")
506-
py_binary(
507-
name = "baz",
508-
...
509-
deps = [
510-
":foo",
511-
] + all_requirements,
517+
In addition to the `requirement` macro, which is used to access the generated `py_library`
518+
target generated from a package's wheel, The generated `requirements.bzl` file contains
519+
functionality for exposing [entry points][whl_ep] as `py_binary` targets as well.
520+
521+
[whl_ep]: https://packaging.python.org/specifications/entry-points/
522+
523+
```starlark
524+
load("@pip_deps//:requirements.bzl", "entry_point")
525+
526+
alias(
527+
name = "pip-compile",
528+
actual = entry_point(
529+
pkg = "pip-tools",
530+
script = "pip-compile",
531+
),
512532
)
513533
```
534+
535+
Note that for packages whose name and script are the same, only the name of the package
536+
is needed when calling the `entry_point` macro.
537+
538+
```starlark
539+
load("@pip_deps//:requirements.bzl", "entry_point")
540+
541+
alias(
542+
name = "flake8",
543+
actual = entry_point("flake8"),
544+
)
545+
```
546+
547+
## Vendoring the requirements.bzl file
548+
549+
In some cases you may not want to generate the requirements.bzl file as a repository rule
550+
while Bazel is fetching dependencies. For example, if you produce a reusable Bazel module
551+
such as a ruleset, you may want to include the requirements.bzl file rather than make your users
552+
install the WORKSPACE setup to generate it.
553+
See https://github.com/bazelbuild/rules_python/issues/608
554+
555+
This is the same workflow as Gazelle, which creates `go_repository` rules with
556+
[`update-repos`](https://github.com/bazelbuild/bazel-gazelle#update-repos)
557+
558+
To do this, use the "write to source file" pattern documented in
559+
https://blog.aspect.dev/bazel-can-write-to-the-source-folder
560+
to put a copy of the generated requirements.bzl into your project.
561+
Then load the requirements.bzl file directly rather than from the generated repository.
562+
See the example in rules_python/examples/pip_parse_vendored.
514563
""",
515564
implementation = _pip_repository_impl,
516565
environ = common_env,

python/pip_install/repositories.bzl

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@
1414

1515
""
1616

17-
load("@bazel_skylib//lib:versions.bzl", "versions")
1817
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
1918
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
20-
load("//:version.bzl", "MINIMUM_BAZEL_VERSION")
2119

2220
_RULE_DEPS = [
2321
# START: maintained by 'bazel run //tools/private:update_pip_deps'
@@ -137,13 +135,6 @@ def pip_install_dependencies():
137135
138136
(However we call it from pip_install, making it optional for users to do so.)
139137
"""
140-
141-
# We only support Bazel LTS and rolling releases.
142-
# Give the user an obvious error to upgrade rather than some obscure missing symbol later.
143-
# It's not guaranteed that users call this function, but it's used by all the pip fetch
144-
# repository rules so it's likely that most users get the right error.
145-
versions.check(MINIMUM_BAZEL_VERSION)
146-
147138
for (name, url, sha256) in _RULE_DEPS:
148139
maybe(
149140
http_archive,

python/repositories.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ For historic reasons, pip_repositories() is defined in //python:pip.bzl.
1919

2020
load("@bazel_tools//tools/build_defs/repo:http.bzl", _http_archive = "http_archive")
2121
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe", "read_netrc", "read_user_netrc", "use_netrc")
22+
load("//python/pip_install:repositories.bzl", "pip_install_dependencies")
2223
load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED")
2324
load("//python/private:coverage_deps.bzl", "coverage_dep")
2425
load("//python/private:full_version.bzl", "full_version")
@@ -59,6 +60,7 @@ def py_repositories():
5960
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
6061
],
6162
)
63+
pip_install_dependencies()
6264

6365
########
6466
# Remaining content of the file is only used to support toolchains.

0 commit comments

Comments
 (0)