xref: /aosp_15_r20/external/bazelbuild-rules_python/python/private/pypi/attrs.bzl (revision 60517a1edbc8ecf509223e9af94a7adec7d736b8)
1# Copyright 2023 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"common attributes for whl_library and pip_repository"
16
17ATTRS = {
18    "download_only": attr.bool(
19        doc = """
20Whether to use "pip download" instead of "pip wheel". Disables building wheels from source, but allows use of
21--platform, --python-version, --implementation, and --abi in --extra_pip_args to download wheels for a different
22platform from the host platform.
23        """,
24    ),
25    "enable_implicit_namespace_pkgs": attr.bool(
26        default = False,
27        doc = """
28If true, disables conversion of native namespace packages into pkg-util style namespace packages. When set all py_binary
29and py_test targets must specify either `legacy_create_init=False` or the global Bazel option
30`--incompatible_default_to_explicit_init_py` to prevent `__init__.py` being automatically generated in every directory.
31
32This option is required to support some packages which cannot handle the conversion to pkg-util style.
33            """,
34    ),
35    "environment": attr.string_dict(
36        doc = """
37Environment variables to set in the pip subprocess.
38Can be used to set common variables such as `http_proxy`, `https_proxy` and `no_proxy`
39Note that pip is run with "--isolated" on the CLI so `PIP_<VAR>_<NAME>`
40style env vars are ignored, but env vars that control requests and urllib3
41can be passed. If you need `PIP_<VAR>_<NAME>`, take a look at `extra_pip_args`
42and `envsubst`.
43        """,
44        default = {},
45    ),
46    "envsubst": attr.string_list(
47        mandatory = False,
48        doc = """\
49A list of environment variables to substitute (e.g. `["PIP_INDEX_URL",
50"PIP_RETRIES"]`). The corresponding variables are expanded in `extra_pip_args`
51using the syntax `$VARNAME` or `${VARNAME}` (expanding to empty string if unset)
52or `${VARNAME:-default}` (expanding to default if the variable is unset or empty
53in the environment). Note: On Bazel 6 and Bazel 7.0 changes to the variables named
54here do not cause packages to be re-fetched. Don't fetch different things based
55on the value of these variables.
56""",
57    ),
58    "experimental_requirement_cycles": attr.string_list_dict(
59        default = {},
60        doc = """\
61A mapping of dependency cycle names to a list of requirements which form that cycle.
62
63Requirements which form cycles will be installed together and taken as
64dependencies together in order to ensure that the cycle is always satisified.
65
66Example:
67  `sphinx` depends on `sphinxcontrib-serializinghtml`
68  When listing both as requirements, ala
69
70  ```
71  py_binary(
72    name = "doctool",
73    ...
74    deps = [
75      "@pypi//sphinx:pkg",
76      "@pypi//sphinxcontrib_serializinghtml",
77     ]
78  )
79  ```
80
81  Will produce a Bazel error such as
82
83  ```
84  ERROR: .../external/pypi_sphinxcontrib_serializinghtml/BUILD.bazel:44:6: in alias rule @pypi_sphinxcontrib_serializinghtml//:pkg: cycle in dependency graph:
85      //:doctool (...)
86      @pypi//sphinxcontrib_serializinghtml:pkg (...)
87  .-> @pypi_sphinxcontrib_serializinghtml//:pkg (...)
88  |   @pypi_sphinxcontrib_serializinghtml//:_pkg (...)
89  |   @pypi_sphinx//:pkg (...)
90  |   @pypi_sphinx//:_pkg (...)
91  `-- @pypi_sphinxcontrib_serializinghtml//:pkg (...)
92  ```
93
94  Which we can resolve by configuring these two requirements to be installed together as a cycle
95
96  ```
97  pip_parse(
98    ...
99    experimental_requirement_cycles = {
100      "sphinx": [
101        "sphinx",
102        "sphinxcontrib-serializinghtml",
103      ]
104    },
105  )
106  ```
107
108Warning:
109  If a dependency participates in multiple cycles, all of those cycles must be
110  collapsed down to one. For instance `a <-> b` and `a <-> c` cannot be listed
111  as two separate cycles.
112""",
113    ),
114    "experimental_target_platforms": attr.string_list(
115        default = [],
116        doc = """\
117A list of platforms that we will generate the conditional dependency graph for
118cross platform wheels by parsing the wheel metadata. This will generate the
119correct dependencies for packages like `sphinx` or `pylint`, which include
120`colorama` when installed and used on Windows platforms.
121
122An empty list means falling back to the legacy behaviour where the host
123platform is the target platform.
124
125WARNING: It may not work as expected in cases where the python interpreter
126implementation that is being used at runtime is different between different platforms.
127This has been tested for CPython only.
128
129For specific target platforms use values of the form `<os>_<arch>` where `<os>`
130is one of `linux`, `osx`, `windows` and arch is one of `x86_64`, `x86_32`,
131`aarch64`, `s390x` and `ppc64le`.
132
133You can also target a specific Python version by using `cp3<minor_version>_<os>_<arch>`.
134If multiple python versions are specified as target platforms, then select statements
135of the `lib` and `whl` targets will include usage of version aware toolchain config
136settings like `@rules_python//python/config_settings:is_python_3.y`.
137
138Special values: `host` (for generating deps for the host platform only) and
139`<prefix>_*` values. For example, `cp39_*`, `linux_*`, `cp39_linux_*`.
140
141NOTE: this is not for cross-compiling Python wheels but rather for parsing the `whl` METADATA correctly.
142""",
143    ),
144    "extra_pip_args": attr.string_list(
145        doc = """Extra arguments to pass on to pip. Must not contain spaces.
146
147Supports environment variables using the syntax `$VARNAME` or
148`${VARNAME}` (expanding to empty string if unset) or
149`${VARNAME:-default}` (expanding to default if the variable is unset
150or empty in the environment), if `"VARNAME"` is listed in the
151`envsubst` attribute. See also `envsubst`.
152""",
153    ),
154    "isolated": attr.bool(
155        doc = """\
156Whether or not to pass the [--isolated](https://pip.pypa.io/en/stable/cli/pip/#cmdoption-isolated) flag to
157the underlying pip command. Alternatively, the {envvar}`RULES_PYTHON_PIP_ISOLATED` environment variable can be used
158to control this flag.
159""",
160        default = True,
161    ),
162    "pip_data_exclude": attr.string_list(
163        doc = "Additional data exclusion parameters to add to the pip packages BUILD file.",
164    ),
165    "python_interpreter": attr.string(
166        doc = """\
167The python interpreter to use. This can either be an absolute path or the name
168of a binary found on the host's `PATH` environment variable. If no value is set
169`python3` is defaulted for Unix systems and `python.exe` for Windows.
170""",
171        # NOTE: This attribute should not have a default. See `_get_python_interpreter_attr`
172        # default = "python3"
173    ),
174    "python_interpreter_target": attr.label(
175        allow_single_file = True,
176        doc = """
177If you are using a custom python interpreter built by another repository rule,
178use this attribute to specify its BUILD target. This allows pip_repository to invoke
179pip using the same interpreter as your toolchain. If set, takes precedence over
180python_interpreter. An example value: "@python3_x86_64-unknown-linux-gnu//:python".
181""",
182    ),
183    "quiet": attr.bool(
184        default = True,
185        doc = """\
186If True, suppress printing stdout and stderr output to the terminal.
187
188If you would like to get more diagnostic output, set
189{envvar}`RULES_PYTHON_REPO_DEBUG=1 <RULES_PYTHON_REPO_DEBUG>`
190or
191{envvar}`RULES_PYTHON_REPO_DEBUG_VERBOSITY=<INFO|DEBUG|TRACE> <RULES_PYTHON_REPO_DEBUG_VERBOSITY>`
192""",
193    ),
194    # 600 is documented as default here: https://docs.bazel.build/versions/master/skylark/lib/repository_ctx.html#execute
195    "timeout": attr.int(
196        default = 600,
197        doc = "Timeout (in seconds) on the rule's execution duration.",
198    ),
199}
200
201def use_isolated(ctx, attr):
202    """Determine whether or not to pass the pip `--isolated` flag to the pip invocation.
203
204    Args:
205        ctx: repository or module context
206        attr: attributes for the repo rule or tag extension
207
208    Returns:
209        True if --isolated should be passed
210    """
211    use_isolated = attr.isolated
212
213    # The environment variable will take precedence over the attribute
214    isolated_env = ctx.os.environ.get("RULES_PYTHON_PIP_ISOLATED", None)
215    if isolated_env != None:
216        if isolated_env.lower() in ("0", "false"):
217            use_isolated = False
218        else:
219            use_isolated = True
220
221    return use_isolated
222