1*60517a1eSAndroid Build Coastguard Worker:::{default-domain} bzl 2*60517a1eSAndroid Build Coastguard Worker::: 3*60517a1eSAndroid Build Coastguard Worker 4*60517a1eSAndroid Build Coastguard Worker# Using dependencies from PyPI 5*60517a1eSAndroid Build Coastguard Worker 6*60517a1eSAndroid Build Coastguard WorkerUsing PyPI packages (aka "pip install") involves two main steps. 7*60517a1eSAndroid Build Coastguard Worker 8*60517a1eSAndroid Build Coastguard Worker1. [Installing third party packages](#installing-third-party-packages) 9*60517a1eSAndroid Build Coastguard Worker2. [Using third party packages as dependencies](#using-third-party-packages) 10*60517a1eSAndroid Build Coastguard Worker 11*60517a1eSAndroid Build Coastguard Worker{#installing-third-party-packages} 12*60517a1eSAndroid Build Coastguard Worker## Installing third party packages 13*60517a1eSAndroid Build Coastguard Worker 14*60517a1eSAndroid Build Coastguard Worker### Using bzlmod 15*60517a1eSAndroid Build Coastguard Worker 16*60517a1eSAndroid Build Coastguard WorkerTo add pip dependencies to your `MODULE.bazel` file, use the `pip.parse` 17*60517a1eSAndroid Build Coastguard Workerextension, and call it to create the central external repo and individual wheel 18*60517a1eSAndroid Build Coastguard Workerexternal repos. Include in the `MODULE.bazel` the toolchain extension as shown 19*60517a1eSAndroid Build Coastguard Workerin the first bzlmod example above. 20*60517a1eSAndroid Build Coastguard Worker 21*60517a1eSAndroid Build Coastguard Worker```starlark 22*60517a1eSAndroid Build Coastguard Workerpip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") 23*60517a1eSAndroid Build Coastguard Workerpip.parse( 24*60517a1eSAndroid Build Coastguard Worker hub_name = "my_deps", 25*60517a1eSAndroid Build Coastguard Worker python_version = "3.11", 26*60517a1eSAndroid Build Coastguard Worker requirements_lock = "//:requirements_lock_3_11.txt", 27*60517a1eSAndroid Build Coastguard Worker) 28*60517a1eSAndroid Build Coastguard Workeruse_repo(pip, "my_deps") 29*60517a1eSAndroid Build Coastguard Worker``` 30*60517a1eSAndroid Build Coastguard WorkerFor more documentation, including how the rules can update/create a requirements 31*60517a1eSAndroid Build Coastguard Workerfile, see the bzlmod examples under the {gh-path}`examples` folder or the documentation 32*60517a1eSAndroid Build Coastguard Workerfor the {obj}`@rules_python//python/extensions:pip.bzl` extension. 33*60517a1eSAndroid Build Coastguard Worker 34*60517a1eSAndroid Build Coastguard Worker```{note} 35*60517a1eSAndroid Build Coastguard WorkerWe are using a host-platform compatible toolchain by default to setup pip dependencies. 36*60517a1eSAndroid Build Coastguard WorkerDuring the setup phase, we create some symlinks, which may be inefficient on Windows 37*60517a1eSAndroid Build Coastguard Workerby default. In that case use the following `.bazelrc` options to improve performance if 38*60517a1eSAndroid Build Coastguard Workeryou have admin privileges: 39*60517a1eSAndroid Build Coastguard Worker 40*60517a1eSAndroid Build Coastguard Worker startup --windows_enable_symlinks 41*60517a1eSAndroid Build Coastguard Worker 42*60517a1eSAndroid Build Coastguard WorkerThis will enable symlinks on Windows and help with bootstrap performance of setting up the 43*60517a1eSAndroid Build Coastguard Workerhermetic host python interpreter on this platform. Linux and OSX users should see no 44*60517a1eSAndroid Build Coastguard Workerdifference. 45*60517a1eSAndroid Build Coastguard Worker``` 46*60517a1eSAndroid Build Coastguard Worker 47*60517a1eSAndroid Build Coastguard Worker### Using a WORKSPACE file 48*60517a1eSAndroid Build Coastguard Worker 49*60517a1eSAndroid Build Coastguard WorkerTo add pip dependencies to your `WORKSPACE`, load the `pip_parse` function and 50*60517a1eSAndroid Build Coastguard Workercall it to create the central external repo and individual wheel external repos. 51*60517a1eSAndroid Build Coastguard Worker 52*60517a1eSAndroid Build Coastguard Worker```starlark 53*60517a1eSAndroid Build Coastguard Workerload("@rules_python//python:pip.bzl", "pip_parse") 54*60517a1eSAndroid Build Coastguard Worker 55*60517a1eSAndroid Build Coastguard Worker# Create a central repo that knows about the dependencies needed from 56*60517a1eSAndroid Build Coastguard Worker# requirements_lock.txt. 57*60517a1eSAndroid Build Coastguard Workerpip_parse( 58*60517a1eSAndroid Build Coastguard Worker name = "my_deps", 59*60517a1eSAndroid Build Coastguard Worker requirements_lock = "//path/to:requirements_lock.txt", 60*60517a1eSAndroid Build Coastguard Worker) 61*60517a1eSAndroid Build Coastguard Worker# Load the starlark macro, which will define your dependencies. 62*60517a1eSAndroid Build Coastguard Workerload("@my_deps//:requirements.bzl", "install_deps") 63*60517a1eSAndroid Build Coastguard Worker# Call it to define repos for your requirements. 64*60517a1eSAndroid Build Coastguard Workerinstall_deps() 65*60517a1eSAndroid Build Coastguard Worker``` 66*60517a1eSAndroid Build Coastguard Worker 67*60517a1eSAndroid Build Coastguard Worker(vendoring-requirements)= 68*60517a1eSAndroid Build Coastguard Worker#### Vendoring the requirements.bzl file 69*60517a1eSAndroid Build Coastguard Worker 70*60517a1eSAndroid Build Coastguard WorkerIn some cases you may not want to generate the requirements.bzl file as a repository rule 71*60517a1eSAndroid Build Coastguard Workerwhile Bazel is fetching dependencies. For example, if you produce a reusable Bazel module 72*60517a1eSAndroid Build Coastguard Workersuch as a ruleset, you may want to include the requirements.bzl file rather than make your users 73*60517a1eSAndroid Build Coastguard Workerinstall the WORKSPACE setup to generate it. 74*60517a1eSAndroid Build Coastguard WorkerSee https://github.com/bazelbuild/rules_python/issues/608 75*60517a1eSAndroid Build Coastguard Worker 76*60517a1eSAndroid Build Coastguard WorkerThis is the same workflow as Gazelle, which creates `go_repository` rules with 77*60517a1eSAndroid Build Coastguard Worker[`update-repos`](https://github.com/bazelbuild/bazel-gazelle#update-repos) 78*60517a1eSAndroid Build Coastguard Worker 79*60517a1eSAndroid Build Coastguard WorkerTo do this, use the "write to source file" pattern documented in 80*60517a1eSAndroid Build Coastguard Workerhttps://blog.aspect.dev/bazel-can-write-to-the-source-folder 81*60517a1eSAndroid Build Coastguard Workerto put a copy of the generated requirements.bzl into your project. 82*60517a1eSAndroid Build Coastguard WorkerThen load the requirements.bzl file directly rather than from the generated repository. 83*60517a1eSAndroid Build Coastguard WorkerSee the example in rules_python/examples/pip_parse_vendored. 84*60517a1eSAndroid Build Coastguard Worker 85*60517a1eSAndroid Build Coastguard Worker(per-os-arch-requirements)= 86*60517a1eSAndroid Build Coastguard Worker### Requirements for a specific OS/Architecture 87*60517a1eSAndroid Build Coastguard Worker 88*60517a1eSAndroid Build Coastguard WorkerIn some cases you may need to use different requirements files for different OS, Arch combinations. This is enabled via the `requirements_by_platform` attribute in `pip.parse` extension and the `pip_parse` repository rule. The keys of the dictionary are labels to the file and the values are a list of comma separated target (os, arch) tuples. 89*60517a1eSAndroid Build Coastguard Worker 90*60517a1eSAndroid Build Coastguard WorkerFor example: 91*60517a1eSAndroid Build Coastguard Worker```starlark 92*60517a1eSAndroid Build Coastguard Worker # ... 93*60517a1eSAndroid Build Coastguard Worker requirements_by_platform = { 94*60517a1eSAndroid Build Coastguard Worker "requirements_linux_x86_64.txt": "linux_x86_64", 95*60517a1eSAndroid Build Coastguard Worker "requirements_osx.txt": "osx_*", 96*60517a1eSAndroid Build Coastguard Worker "requirements_linux_exotic.txt": "linux_exotic", 97*60517a1eSAndroid Build Coastguard Worker "requirements_some_platforms.txt": "linux_aarch64,windows_*", 98*60517a1eSAndroid Build Coastguard Worker }, 99*60517a1eSAndroid Build Coastguard Worker # For the list of standard platforms that the rules_python has toolchains for, default to 100*60517a1eSAndroid Build Coastguard Worker # the following requirements file. 101*60517a1eSAndroid Build Coastguard Worker requirements_lock = "requirements_lock.txt", 102*60517a1eSAndroid Build Coastguard Worker``` 103*60517a1eSAndroid Build Coastguard Worker 104*60517a1eSAndroid Build Coastguard WorkerIn case of duplicate platforms, `rules_python` will raise an error as there has 105*60517a1eSAndroid Build Coastguard Workerto be unambiguous mapping of the requirement files to the (os, arch) tuples. 106*60517a1eSAndroid Build Coastguard Worker 107*60517a1eSAndroid Build Coastguard WorkerAn alternative way is to use per-OS requirement attributes. 108*60517a1eSAndroid Build Coastguard Worker```starlark 109*60517a1eSAndroid Build Coastguard Worker # ... 110*60517a1eSAndroid Build Coastguard Worker requirements_windows = "requirements_windows.txt", 111*60517a1eSAndroid Build Coastguard Worker requirements_darwin = "requirements_darwin.txt", 112*60517a1eSAndroid Build Coastguard Worker # For the remaining platforms (which is basically only linux OS), use this file. 113*60517a1eSAndroid Build Coastguard Worker requirements_lock = "requirements_lock.txt", 114*60517a1eSAndroid Build Coastguard Worker) 115*60517a1eSAndroid Build Coastguard Worker``` 116*60517a1eSAndroid Build Coastguard Worker 117*60517a1eSAndroid Build Coastguard Worker### pip rules 118*60517a1eSAndroid Build Coastguard Worker 119*60517a1eSAndroid Build Coastguard WorkerNote that since `pip_parse` and `pip.parse` are executed at evaluation time, 120*60517a1eSAndroid Build Coastguard WorkerBazel has no information about the Python toolchain and cannot enforce that the 121*60517a1eSAndroid Build Coastguard Workerinterpreter used to invoke `pip` matches the interpreter used to run 122*60517a1eSAndroid Build Coastguard Worker`py_binary` targets. By default, `pip_parse` uses the system command 123*60517a1eSAndroid Build Coastguard Worker`"python3"`. To override this, pass in the `python_interpreter` attribute or 124*60517a1eSAndroid Build Coastguard Worker`python_interpreter_target` attribute to `pip_parse`. The `pip.parse` `bzlmod` extension 125*60517a1eSAndroid Build Coastguard Workerby default uses the hermetic python toolchain for the host platform. 126*60517a1eSAndroid Build Coastguard Worker 127*60517a1eSAndroid Build Coastguard WorkerYou can have multiple `pip_parse`s in the same workspace, or use the pip 128*60517a1eSAndroid Build Coastguard Workerextension multiple times when using bzlmod. This configuration will create 129*60517a1eSAndroid Build Coastguard Workermultiple external repos that have no relation to one another and may result in 130*60517a1eSAndroid Build Coastguard Workerdownloading the same wheels numerous times. 131*60517a1eSAndroid Build Coastguard Worker 132*60517a1eSAndroid Build Coastguard WorkerAs with any repository rule, if you would like to ensure that `pip_parse` is 133*60517a1eSAndroid Build Coastguard Workerre-executed to pick up a non-hermetic change to your environment (e.g., updating 134*60517a1eSAndroid Build Coastguard Workeryour system `python` interpreter), you can force it to re-execute by running 135*60517a1eSAndroid Build Coastguard Worker`bazel sync --only [pip_parse name]`. 136*60517a1eSAndroid Build Coastguard Worker 137*60517a1eSAndroid Build Coastguard Worker{#using-third-party-packages} 138*60517a1eSAndroid Build Coastguard Worker## Using third party packages as dependencies 139*60517a1eSAndroid Build Coastguard Worker 140*60517a1eSAndroid Build Coastguard WorkerEach extracted wheel repo contains a `py_library` target representing 141*60517a1eSAndroid Build Coastguard Workerthe wheel's contents. There are two ways to access this library. The 142*60517a1eSAndroid Build Coastguard Workerfirst uses the `requirement()` function defined in the central 143*60517a1eSAndroid Build Coastguard Workerrepo's `//:requirements.bzl` file. This function maps a pip package 144*60517a1eSAndroid Build Coastguard Workername to a label: 145*60517a1eSAndroid Build Coastguard Worker 146*60517a1eSAndroid Build Coastguard Worker```starlark 147*60517a1eSAndroid Build Coastguard Workerload("@my_deps//:requirements.bzl", "requirement") 148*60517a1eSAndroid Build Coastguard Worker 149*60517a1eSAndroid Build Coastguard Workerpy_library( 150*60517a1eSAndroid Build Coastguard Worker name = "mylib", 151*60517a1eSAndroid Build Coastguard Worker srcs = ["mylib.py"], 152*60517a1eSAndroid Build Coastguard Worker deps = [ 153*60517a1eSAndroid Build Coastguard Worker ":myotherlib", 154*60517a1eSAndroid Build Coastguard Worker requirement("some_pip_dep"), 155*60517a1eSAndroid Build Coastguard Worker requirement("another_pip_dep"), 156*60517a1eSAndroid Build Coastguard Worker ] 157*60517a1eSAndroid Build Coastguard Worker) 158*60517a1eSAndroid Build Coastguard Worker``` 159*60517a1eSAndroid Build Coastguard Worker 160*60517a1eSAndroid Build Coastguard WorkerThe reason `requirement()` exists is to insulate from 161*60517a1eSAndroid Build Coastguard Workerchanges to the underlying repository and label strings. However, those 162*60517a1eSAndroid Build Coastguard Workerlabels have become directly used, so aren't able to easily change regardless. 163*60517a1eSAndroid Build Coastguard Worker 164*60517a1eSAndroid Build Coastguard WorkerOn the other hand, using `requirement()` has several drawbacks; see 165*60517a1eSAndroid Build Coastguard Worker[this issue][requirements-drawbacks] for an enumeration. If you don't 166*60517a1eSAndroid Build Coastguard Workerwant to use `requirement()`, you can use the library 167*60517a1eSAndroid Build Coastguard Workerlabels directly instead. For `pip_parse`, the labels are of the following form: 168*60517a1eSAndroid Build Coastguard Worker 169*60517a1eSAndroid Build Coastguard Worker```starlark 170*60517a1eSAndroid Build Coastguard Worker@{name}//{package} 171*60517a1eSAndroid Build Coastguard Worker``` 172*60517a1eSAndroid Build Coastguard Worker 173*60517a1eSAndroid Build Coastguard WorkerHere `name` is the `name` attribute that was passed to `pip_parse` and 174*60517a1eSAndroid Build Coastguard Worker`package` is the pip package name with characters that are illegal in 175*60517a1eSAndroid Build Coastguard WorkerBazel label names (e.g. `-`, `.`) replaced with `_`. If you need to 176*60517a1eSAndroid Build Coastguard Workerupdate `name` from "old" to "new", then you can run the following 177*60517a1eSAndroid Build Coastguard Workerbuildozer command: 178*60517a1eSAndroid Build Coastguard Worker 179*60517a1eSAndroid Build Coastguard Worker```shell 180*60517a1eSAndroid Build Coastguard Workerbuildozer 'substitute deps @old//([^/]+) @new//${1}' //...:* 181*60517a1eSAndroid Build Coastguard Worker``` 182*60517a1eSAndroid Build Coastguard Worker 183*60517a1eSAndroid Build Coastguard Worker[requirements-drawbacks]: https://github.com/bazelbuild/rules_python/issues/414 184*60517a1eSAndroid Build Coastguard Worker 185*60517a1eSAndroid Build Coastguard Worker### Entry points 186*60517a1eSAndroid Build Coastguard Worker 187*60517a1eSAndroid Build Coastguard WorkerIf you would like to access [entry points][whl_ep], see the `py_console_script_binary` rule documentation, 188*60517a1eSAndroid Build Coastguard Workerwhich can help you create a `py_binary` target for a particular console script exposed by a package. 189*60517a1eSAndroid Build Coastguard Worker 190*60517a1eSAndroid Build Coastguard Worker[whl_ep]: https://packaging.python.org/specifications/entry-points/ 191*60517a1eSAndroid Build Coastguard Worker 192*60517a1eSAndroid Build Coastguard Worker### 'Extras' dependencies 193*60517a1eSAndroid Build Coastguard Worker 194*60517a1eSAndroid Build Coastguard WorkerAny 'extras' specified in the requirements lock file will be automatically added 195*60517a1eSAndroid Build Coastguard Workeras transitive dependencies of the package. In the example above, you'd just put 196*60517a1eSAndroid Build Coastguard Worker`requirement("useful_dep")` or `@pypi//useful_dep`. 197*60517a1eSAndroid Build Coastguard Worker 198*60517a1eSAndroid Build Coastguard Worker### Consuming Wheel Dists Directly 199*60517a1eSAndroid Build Coastguard Worker 200*60517a1eSAndroid Build Coastguard WorkerIf you need to depend on the wheel dists themselves, for instance, to pass them 201*60517a1eSAndroid Build Coastguard Workerto some other packaging tool, you can get a handle to them with the 202*60517a1eSAndroid Build Coastguard Worker`whl_requirement` macro. For example: 203*60517a1eSAndroid Build Coastguard Worker 204*60517a1eSAndroid Build Coastguard Worker```starlark 205*60517a1eSAndroid Build Coastguard Workerload("@pypi//:requirements.bzl", "whl_requirement") 206*60517a1eSAndroid Build Coastguard Worker 207*60517a1eSAndroid Build Coastguard Workerfilegroup( 208*60517a1eSAndroid Build Coastguard Worker name = "whl_files", 209*60517a1eSAndroid Build Coastguard Worker data = [ 210*60517a1eSAndroid Build Coastguard Worker # This is equivalent to "@pypi//boto3:whl" 211*60517a1eSAndroid Build Coastguard Worker whl_requirement("boto3"), 212*60517a1eSAndroid Build Coastguard Worker ] 213*60517a1eSAndroid Build Coastguard Worker) 214*60517a1eSAndroid Build Coastguard Worker``` 215*60517a1eSAndroid Build Coastguard Worker 216*60517a1eSAndroid Build Coastguard Worker### Creating a filegroup of files within a whl 217*60517a1eSAndroid Build Coastguard Worker 218*60517a1eSAndroid Build Coastguard WorkerThe rule {obj}`whl_filegroup` exists as an easy way to extract the necessary files 219*60517a1eSAndroid Build Coastguard Workerfrom a whl file without the need to modify the `BUILD.bazel` contents of the 220*60517a1eSAndroid Build Coastguard Workerwhl repositories generated via `pip_repository`. Use it similarly to the `filegroup` 221*60517a1eSAndroid Build Coastguard Workerabove. See the API docs for more information. 222*60517a1eSAndroid Build Coastguard Worker 223*60517a1eSAndroid Build Coastguard Worker(advance-topics)= 224*60517a1eSAndroid Build Coastguard Worker## Advanced topics 225*60517a1eSAndroid Build Coastguard Worker 226*60517a1eSAndroid Build Coastguard Worker(circular-deps)= 227*60517a1eSAndroid Build Coastguard Worker### Circular dependencies 228*60517a1eSAndroid Build Coastguard Worker 229*60517a1eSAndroid Build Coastguard WorkerSometimes PyPi packages contain dependency cycles -- for instance a particular 230*60517a1eSAndroid Build Coastguard Workerversion `sphinx` (this is no longer the case in the latest version as of 231*60517a1eSAndroid Build Coastguard Worker2024-06-02) depends on `sphinxcontrib-serializinghtml`. When using them as 232*60517a1eSAndroid Build Coastguard Worker`requirement()`s, ala 233*60517a1eSAndroid Build Coastguard Worker 234*60517a1eSAndroid Build Coastguard Worker``` 235*60517a1eSAndroid Build Coastguard Workerpy_binary( 236*60517a1eSAndroid Build Coastguard Worker name = "doctool", 237*60517a1eSAndroid Build Coastguard Worker ... 238*60517a1eSAndroid Build Coastguard Worker deps = [ 239*60517a1eSAndroid Build Coastguard Worker requirement("sphinx"), 240*60517a1eSAndroid Build Coastguard Worker ], 241*60517a1eSAndroid Build Coastguard Worker) 242*60517a1eSAndroid Build Coastguard Worker``` 243*60517a1eSAndroid Build Coastguard Worker 244*60517a1eSAndroid Build Coastguard WorkerBazel will protest because it doesn't support cycles in the build graph -- 245*60517a1eSAndroid Build Coastguard Worker 246*60517a1eSAndroid Build Coastguard Worker``` 247*60517a1eSAndroid Build Coastguard WorkerERROR: .../external/pypi_sphinxcontrib_serializinghtml/BUILD.bazel:44:6: in alias rule @pypi_sphinxcontrib_serializinghtml//:pkg: cycle in dependency graph: 248*60517a1eSAndroid Build Coastguard Worker //:doctool (...) 249*60517a1eSAndroid Build Coastguard Worker @pypi//sphinxcontrib_serializinghtml:pkg (...) 250*60517a1eSAndroid Build Coastguard Worker.-> @pypi_sphinxcontrib_serializinghtml//:pkg (...) 251*60517a1eSAndroid Build Coastguard Worker| @pypi_sphinxcontrib_serializinghtml//:_pkg (...) 252*60517a1eSAndroid Build Coastguard Worker| @pypi_sphinx//:pkg (...) 253*60517a1eSAndroid Build Coastguard Worker| @pypi_sphinx//:_pkg (...) 254*60517a1eSAndroid Build Coastguard Worker`-- @pypi_sphinxcontrib_serializinghtml//:pkg (...) 255*60517a1eSAndroid Build Coastguard Worker``` 256*60517a1eSAndroid Build Coastguard Worker 257*60517a1eSAndroid Build Coastguard WorkerThe `experimental_requirement_cycles` argument allows you to work around these 258*60517a1eSAndroid Build Coastguard Workerissues by specifying groups of packages which form cycles. `pip_parse` will 259*60517a1eSAndroid Build Coastguard Workertransparently fix the cycles for you and provide the cyclic dependencies 260*60517a1eSAndroid Build Coastguard Workersimultaneously. 261*60517a1eSAndroid Build Coastguard Worker 262*60517a1eSAndroid Build Coastguard Worker```starlark 263*60517a1eSAndroid Build Coastguard Workerpip_parse( 264*60517a1eSAndroid Build Coastguard Worker ... 265*60517a1eSAndroid Build Coastguard Worker experimental_requirement_cycles = { 266*60517a1eSAndroid Build Coastguard Worker "sphinx": [ 267*60517a1eSAndroid Build Coastguard Worker "sphinx", 268*60517a1eSAndroid Build Coastguard Worker "sphinxcontrib-serializinghtml", 269*60517a1eSAndroid Build Coastguard Worker ] 270*60517a1eSAndroid Build Coastguard Worker }, 271*60517a1eSAndroid Build Coastguard Worker) 272*60517a1eSAndroid Build Coastguard Worker``` 273*60517a1eSAndroid Build Coastguard Worker 274*60517a1eSAndroid Build Coastguard Worker`pip_parse` supports fixing multiple cycles simultaneously, however cycles must 275*60517a1eSAndroid Build Coastguard Workerbe distinct. `apache-airflow` for instance has dependency cycles with a number 276*60517a1eSAndroid Build Coastguard Workerof its optional dependencies, which means those optional dependencies must all 277*60517a1eSAndroid Build Coastguard Workerbe a part of the `airflow` cycle. For instance -- 278*60517a1eSAndroid Build Coastguard Worker 279*60517a1eSAndroid Build Coastguard Worker```starlark 280*60517a1eSAndroid Build Coastguard Workerpip_parse( 281*60517a1eSAndroid Build Coastguard Worker ... 282*60517a1eSAndroid Build Coastguard Worker experimental_requirement_cycles = { 283*60517a1eSAndroid Build Coastguard Worker "airflow": [ 284*60517a1eSAndroid Build Coastguard Worker "apache-airflow", 285*60517a1eSAndroid Build Coastguard Worker "apache-airflow-providers-common-sql", 286*60517a1eSAndroid Build Coastguard Worker "apache-airflow-providers-postgres", 287*60517a1eSAndroid Build Coastguard Worker "apache-airflow-providers-sqlite", 288*60517a1eSAndroid Build Coastguard Worker ] 289*60517a1eSAndroid Build Coastguard Worker } 290*60517a1eSAndroid Build Coastguard Worker) 291*60517a1eSAndroid Build Coastguard Worker``` 292*60517a1eSAndroid Build Coastguard Worker 293*60517a1eSAndroid Build Coastguard WorkerAlternatively, one could resolve the cycle by removing one leg of it. 294*60517a1eSAndroid Build Coastguard Worker 295*60517a1eSAndroid Build Coastguard WorkerFor example while `apache-airflow-providers-sqlite` is "baked into" the Airflow 296*60517a1eSAndroid Build Coastguard Workerpackage, `apache-airflow-providers-postgres` is not and is an optional feature. 297*60517a1eSAndroid Build Coastguard WorkerRather than listing `apache-airflow[postgres]` in your `requirements.txt` which 298*60517a1eSAndroid Build Coastguard Workerwould expose a cycle via the extra, one could either _manually_ depend on 299*60517a1eSAndroid Build Coastguard Worker`apache-airflow` and `apache-airflow-providers-postgres` separately as 300*60517a1eSAndroid Build Coastguard Workerrequirements. Bazel rules which need only `apache-airflow` can take it as a 301*60517a1eSAndroid Build Coastguard Workerdependency, and rules which explicitly want to mix in 302*60517a1eSAndroid Build Coastguard Worker`apache-airflow-providers-postgres` now can. 303*60517a1eSAndroid Build Coastguard Worker 304*60517a1eSAndroid Build Coastguard WorkerAlternatively, one could use `rules_python`'s patching features to remove one 305*60517a1eSAndroid Build Coastguard Workerleg of the dependency manually. For instance by making 306*60517a1eSAndroid Build Coastguard Worker`apache-airflow-providers-postgres` not explicitly depend on `apache-airflow` or 307*60517a1eSAndroid Build Coastguard Workerperhaps `apache-airflow-providers-common-sql`. 308*60517a1eSAndroid Build Coastguard Worker 309*60517a1eSAndroid Build Coastguard Worker 310*60517a1eSAndroid Build Coastguard Worker(bazel-downloader)= 311*60517a1eSAndroid Build Coastguard Worker### Bazel downloader and multi-platform wheel hub repository. 312*60517a1eSAndroid Build Coastguard Worker 313*60517a1eSAndroid Build Coastguard WorkerThe `bzlmod` `pip.parse` call supports pulling information from `PyPI` (or a 314*60517a1eSAndroid Build Coastguard Workercompatible mirror) and it will ensure that the [bazel 315*60517a1eSAndroid Build Coastguard Workerdownloader][bazel_downloader] is used for downloading the wheels. This allows 316*60517a1eSAndroid Build Coastguard Workerthe users to use the [credential helper](#credential-helper) to authenticate 317*60517a1eSAndroid Build Coastguard Workerwith the mirror and it also ensures that the distribution downloads are cached. 318*60517a1eSAndroid Build Coastguard WorkerIt also avoids using `pip` altogether and results in much faster dependency 319*60517a1eSAndroid Build Coastguard Workerfetching. 320*60517a1eSAndroid Build Coastguard Worker 321*60517a1eSAndroid Build Coastguard WorkerThis can be enabled by `experimental_index_url` and related flags as shown in 322*60517a1eSAndroid Build Coastguard Workerthe {gh-path}`examples/bzlmod/MODULE.bazel` example. 323*60517a1eSAndroid Build Coastguard Worker 324*60517a1eSAndroid Build Coastguard WorkerWhen using this feature during the `pip` extension evaluation you will see the accessed indexes similar to below: 325*60517a1eSAndroid Build Coastguard Worker```console 326*60517a1eSAndroid Build Coastguard WorkerLoading: 0 packages loaded 327*60517a1eSAndroid Build Coastguard Worker currently loading: docs/ 328*60517a1eSAndroid Build Coastguard Worker Fetching module extension pip in @@//python/extensions:pip.bzl; starting 329*60517a1eSAndroid Build Coastguard Worker Fetching https://pypi.org/simple/twine/ 330*60517a1eSAndroid Build Coastguard Worker``` 331*60517a1eSAndroid Build Coastguard Worker 332*60517a1eSAndroid Build Coastguard WorkerThis does not mean that `rules_python` is fetching the wheels eagerly, but it 333*60517a1eSAndroid Build Coastguard Workerrather means that it is calling the PyPI server to get the Simple API response 334*60517a1eSAndroid Build Coastguard Workerto get the list of all available source and wheel distributions. Once it has 335*60517a1eSAndroid Build Coastguard Workergot all of the available distributions, it will select the right ones depending 336*60517a1eSAndroid Build Coastguard Workeron the `sha256` values in your `requirements_lock.txt` file. The compatible 337*60517a1eSAndroid Build Coastguard Workerdistribution URLs will be then written to the `MODULE.bazel.lock` file. Currently 338*60517a1eSAndroid Build Coastguard Workerusers wishing to use the lock file with `rules_python` with this feature have 339*60517a1eSAndroid Build Coastguard Workerto set an environment variable `RULES_PYTHON_OS_ARCH_LOCK_FILE=0` which will 340*60517a1eSAndroid Build Coastguard Workerbecome default in the next release. 341*60517a1eSAndroid Build Coastguard Worker 342*60517a1eSAndroid Build Coastguard WorkerFetching the distribution information from the PyPI allows `rules_python` to 343*60517a1eSAndroid Build Coastguard Workerknow which `whl` should be used on which target platform and it will determine 344*60517a1eSAndroid Build Coastguard Workerthat by parsing the `whl` filename based on [PEP600], [PEP656] standards. This 345*60517a1eSAndroid Build Coastguard Workerallows the user to configure the behaviour by using the following publicly 346*60517a1eSAndroid Build Coastguard Workeravailable flags: 347*60517a1eSAndroid Build Coastguard Worker* {obj}`--@rules_python//python/config_settings:py_linux_libc` for selecting the Linux libc variant. 348*60517a1eSAndroid Build Coastguard Worker* {obj}`--@rules_python//python/config_settings:pip_whl` for selecting `whl` distribution preference. 349*60517a1eSAndroid Build Coastguard Worker* {obj}`--@rules_python//python/config_settings:pip_whl_osx_arch` for selecting MacOS wheel preference. 350*60517a1eSAndroid Build Coastguard Worker* {obj}`--@rules_python//python/config_settings:pip_whl_glibc_version` for selecting the GLIBC version compatibility. 351*60517a1eSAndroid Build Coastguard Worker* {obj}`--@rules_python//python/config_settings:pip_whl_muslc_version` for selecting the musl version compatibility. 352*60517a1eSAndroid Build Coastguard Worker* {obj}`--@rules_python//python/config_settings:pip_whl_osx_version` for selecting MacOS version compatibility. 353*60517a1eSAndroid Build Coastguard Worker 354*60517a1eSAndroid Build Coastguard Worker[bazel_downloader]: https://bazel.build/rules/lib/builtins/repository_ctx#download 355*60517a1eSAndroid Build Coastguard Worker[pep600]: https://peps.python.org/pep-0600/ 356*60517a1eSAndroid Build Coastguard Worker[pep656]: https://peps.python.org/pep-0656/ 357*60517a1eSAndroid Build Coastguard Worker 358*60517a1eSAndroid Build Coastguard Worker(credential-helper)= 359*60517a1eSAndroid Build Coastguard Worker### Credential Helper 360*60517a1eSAndroid Build Coastguard Worker 361*60517a1eSAndroid Build Coastguard WorkerThe "use Bazel downloader for python wheels" experimental feature includes support for the Bazel 362*60517a1eSAndroid Build Coastguard Worker[Credential Helper][cred-helper-design]. 363*60517a1eSAndroid Build Coastguard Worker 364*60517a1eSAndroid Build Coastguard WorkerYour python artifact registry may provide a credential helper for you. Refer to your index's docs 365*60517a1eSAndroid Build Coastguard Workerto see if one is provided. 366*60517a1eSAndroid Build Coastguard Worker 367*60517a1eSAndroid Build Coastguard WorkerSee the [Credential Helper Spec][cred-helper-spec] for details. 368*60517a1eSAndroid Build Coastguard Worker 369*60517a1eSAndroid Build Coastguard Worker[cred-helper-design]: https://github.com/bazelbuild/proposals/blob/main/designs/2022-06-07-bazel-credential-helpers.md 370*60517a1eSAndroid Build Coastguard Worker[cred-helper-spec]: https://github.com/EngFlow/credential-helper-spec/blob/main/spec.md 371*60517a1eSAndroid Build Coastguard Worker 372*60517a1eSAndroid Build Coastguard Worker 373*60517a1eSAndroid Build Coastguard Worker#### Basic Example: 374*60517a1eSAndroid Build Coastguard Worker 375*60517a1eSAndroid Build Coastguard WorkerThe simplest form of a credential helper is a bash script that accepts an arg and spits out JSON to 376*60517a1eSAndroid Build Coastguard Workerstdout. For a service like Google Artifact Registry that uses ['Basic' HTTP Auth][rfc7617] and does 377*60517a1eSAndroid Build Coastguard Workernot provide a credential helper that conforms to the [spec][cred-helper-spec], the script might 378*60517a1eSAndroid Build Coastguard Workerlook like: 379*60517a1eSAndroid Build Coastguard Worker 380*60517a1eSAndroid Build Coastguard Worker```bash 381*60517a1eSAndroid Build Coastguard Worker#!/bin/bash 382*60517a1eSAndroid Build Coastguard Worker# cred_helper.sh 383*60517a1eSAndroid Build Coastguard WorkerARG=$1 # but we don't do anything with it as it's always "get" 384*60517a1eSAndroid Build Coastguard Worker 385*60517a1eSAndroid Build Coastguard Worker# formatting is optional 386*60517a1eSAndroid Build Coastguard Workerecho '{' 387*60517a1eSAndroid Build Coastguard Workerecho ' "headers": {' 388*60517a1eSAndroid Build Coastguard Workerecho ' "Authorization": ["Basic dGVzdDoxMjPCow=="]' 389*60517a1eSAndroid Build Coastguard Workerecho ' }' 390*60517a1eSAndroid Build Coastguard Workerecho '}' 391*60517a1eSAndroid Build Coastguard Worker``` 392*60517a1eSAndroid Build Coastguard Worker 393*60517a1eSAndroid Build Coastguard WorkerConfigure Bazel to use this credential helper for your python index `example.com`: 394*60517a1eSAndroid Build Coastguard Worker 395*60517a1eSAndroid Build Coastguard Worker``` 396*60517a1eSAndroid Build Coastguard Worker# .bazelrc 397*60517a1eSAndroid Build Coastguard Workerbuild --credential_helper=example.com=/full/path/to/cred_helper.sh 398*60517a1eSAndroid Build Coastguard Worker``` 399*60517a1eSAndroid Build Coastguard Worker 400*60517a1eSAndroid Build Coastguard WorkerBazel will call this file like `cred_helper.sh get` and use the returned JSON to inject headers 401*60517a1eSAndroid Build Coastguard Workerinto whatever HTTP(S) request it performs against `example.com`. 402*60517a1eSAndroid Build Coastguard Worker 403*60517a1eSAndroid Build Coastguard Worker[rfc7617]: https://datatracker.ietf.org/doc/html/rfc7617 404