11(common-deps-with-multiple-pypi-versions)=
22# How to use a common set of dependencies with multiple PyPI versions
33
4- In this guide, we show how to handle a situation common to monorepos
5- that extensively share code: How does a common library refer to the correct
6- ` @pypi_<name> ` hub when binaries may have their own requirements (and thus
7- PyPI hub name)? Stated as code, this situation:
4+ In this guide, we show how to handle a situation common to monorepos that
5+ extensively share code: How do multiple binaries utilize distinct requirements
6+ files while pulling from shared internal libraries, without requiring
7+ manually-maintained ` select() ` logic inside dependency targets?
88
9- ``` bzl
9+ Stated as code, consider this example:
1010
11+ ``` bzl
12+ # When building bin_alpha, requests and more_itertools should resolve
13+ # from requirements_alpha.txt
1114py_binary(
12- name = " bin_alpha" ,
13- deps = [" @pypi_alpha//requests" , " :common" ],
15+ name = " bin_alpha" ,
16+ deps = [
17+ " @pypi//requests" ,
18+ " :common" ,
19+ ],
1420)
21+
22+ # When building bin_beta, requests and more_itertools should resolve
23+ # from requirements_beta.txt
1524py_binary(
16- name = " bin_beta" ,
17- deps = [" @pypi_beta//requests" , " :common" ],
25+ name = " bin_beta" ,
26+ deps = [
27+ " @pypi//requests" ,
28+ " :common" ,
29+ ],
1830)
1931
32+ # Transitive dependencies like more_itertools are requested here, but
33+ # must automatically match whichever dependency track is active for the binary.
2034py_library(
21- name = " common" ,
22- deps = [" @pypi_??? //more_itertools" ] # <-- Which @pypi repo?
35+ name = " common" ,
36+ deps = [" @pypi //more_itertools" ],
2337)
2438```
2539
26- ## Using flags to pick a hub
40+ ## Defining dependency tracks via custom platforms
2741
28- The basic trick to make ` :common ` pick the appropriate ` @pypi_<name> ` is to use
29- ` select() ` to choose one based on build flags. To help this process, ` py_binary `
30- et al allow forcing particular build flags to be used, and custom flags can be
31- registered to allow ` py_binary ` et al to set them.
42+ The solution involves defining custom "platforms" mapped to separate
43+ dependency tracks inside ` MODULE.bazel ` . Using custom platforms via
44+ {obj}` pip.default ` and associating requirements files to them through the
45+ ` requirements_by_platform ` attribute on {obj}` pip.parse ` instructs
46+ ` rules_python ` to generate ` select() ` logic behind a unified hub.
3247
33- In this example, we create a custom string flag named ` //:pypi_hub ` ,
34- register it to allow using it with ` py_binary ` directly, then use ` select() `
35- to pick different dependencies.
48+ Binaries configure their execution requirements by forcing flag transition
49+ attributes using custom build setting flags.
3650
37- ``` bzl
51+ In this example, we define custom string flag named ` //:pypi_hub ` , setup
52+ distinct custom platforms for ` "alpha" ` and ` "beta" ` profiles, and register
53+ associated requirements lock files grouped inside the ` @pypi ` hub.
54+
55+ ``` starlark
3856# File: MODULE.bazel
3957
58+ rules_python_config = use_extension(
59+ " @rules_python//python/extensions:config.bzl" ,
60+ " config" ,
61+ )
4062rules_python_config.add_transition_setting(
4163 setting = " //:pypi_hub" ,
4264)
4365
66+ pip = use_extension(" @rules_python//python/extensions:pip.bzl" , " pip" )
67+
68+ pip.default(
69+ platform = " alpha" ,
70+ config_settings = [" @//:is_pypi_alpha" ],
71+ )
72+
73+ pip.default(
74+ platform = " beta" ,
75+ config_settings = [" @//:is_pypi_beta" ],
76+ )
77+
78+ pip.parse(
79+ hub_name = " pypi" ,
80+ python_version = " 3.14" ,
81+ requirements_by_platform = {
82+ " //:requirements_alpha.txt" : " alpha" ,
83+ " //:requirements_beta.txt" : " beta" ,
84+ },
85+ )
86+
87+ use_repo(pip, " pypi" )
88+ ```
89+
90+ ``` starlark
4491# File: BUILD.bazel
4592
4693load(" @bazel_skylib//rules:common_settings.bzl" , " string_flag" )
4794
4895string_flag(
4996 name = " pypi_hub" ,
97+ build_setting_default = " none" ,
5098)
5199
52100config_setting(
@@ -56,7 +104,7 @@ config_setting(
56104
57105config_setting(
58106 name = " is_pypi_beta" ,
59- flag_values = {" //:pypi_hub" : " beta" }
107+ flag_values = {" //:pypi_hub" : " beta" },
60108)
61109
62110py_binary(
@@ -65,26 +113,32 @@ py_binary(
65113 config_settings = {
66114 " //:pypi_hub" : " alpha" ,
67115 },
68- deps = [" @pypi_alpha//requests" , " :common" ],
116+ deps = [
117+ " @pypi//requests" ,
118+ " :common" ,
119+ ],
69120)
121+
70122py_binary(
71123 name = " bin_beta" ,
72124 srcs = [" bin_beta.py" ],
73125 config_settings = {
74126 " //:pypi_hub" : " beta" ,
75127 },
76- deps = [" @pypi_beta//requests" , " :common" ],
128+ deps = [
129+ " @pypi//requests" ,
130+ " :common" ,
131+ ],
77132)
133+
78134py_library(
79135 name = " common" ,
80- deps = select({
81- " :is_pypi_alpha" : [" @pypi_alpha//more_itertools" ],
82- " :is_pypi_beta" : [" @pypi_beta//more_itertools" ],
83- }),
136+ deps = [" @pypi//more_itertools" ],
84137)
85138```
86139
87- When ` bin_alpha ` and ` bin_beta ` are built, they will have the ` pypi_hub `
88- flag force to their respective value. When ` :common ` is evaluated, it sees
89- the flag value of the binary that is consuming it, and the ` select() ` resolves
90- appropriately.
140+ When building ` bin_alpha ` or ` bin_beta ` , they set ` //:pypi_hub ` via target
141+ transitions. The generated aliased dependencies inside the ` @pypi ` hub will
142+ evaluate that Bazel configuration, automatically delivering corresponding
143+ Python wheels from targeted lock files.
144+
0 commit comments