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_hub_aliases": attr.string_list_dict( 145 doc = """\ 146Extra aliases to make for specific wheels in the hub repo. This is useful when 147paired with the {attr}`whl_modifications`. 148 149:::{versionadded} 0.38.0 150::: 151""", 152 mandatory = False, 153 ), 154 "extra_pip_args": attr.string_list( 155 doc = """Extra arguments to pass on to pip. Must not contain spaces. 156 157Supports environment variables using the syntax `$VARNAME` or 158`${VARNAME}` (expanding to empty string if unset) or 159`${VARNAME:-default}` (expanding to default if the variable is unset 160or empty in the environment), if `"VARNAME"` is listed in the 161`envsubst` attribute. See also `envsubst`. 162""", 163 ), 164 "isolated": attr.bool( 165 doc = """\ 166Whether or not to pass the [--isolated](https://pip.pypa.io/en/stable/cli/pip/#cmdoption-isolated) flag to 167the underlying pip command. Alternatively, the {envvar}`RULES_PYTHON_PIP_ISOLATED` environment variable can be used 168to control this flag. 169""", 170 default = True, 171 ), 172 "pip_data_exclude": attr.string_list( 173 doc = "Additional data exclusion parameters to add to the pip packages BUILD file.", 174 ), 175 "python_interpreter": attr.string( 176 doc = """\ 177The python interpreter to use. This can either be an absolute path or the name 178of a binary found on the host's `PATH` environment variable. If no value is set 179`python3` is defaulted for Unix systems and `python.exe` for Windows. 180""", 181 # NOTE: This attribute should not have a default. See `_get_python_interpreter_attr` 182 # default = "python3" 183 ), 184 "python_interpreter_target": attr.label( 185 allow_single_file = True, 186 doc = """ 187If you are using a custom python interpreter built by another repository rule, 188use this attribute to specify its BUILD target. This allows pip_repository to invoke 189pip using the same interpreter as your toolchain. If set, takes precedence over 190python_interpreter. An example value: "@python3_x86_64-unknown-linux-gnu//:python". 191""", 192 ), 193 "quiet": attr.bool( 194 default = True, 195 doc = """\ 196If True, suppress printing stdout and stderr output to the terminal. 197 198If you would like to get more diagnostic output, set 199{envvar}`RULES_PYTHON_REPO_DEBUG=1 <RULES_PYTHON_REPO_DEBUG>` 200or 201{envvar}`RULES_PYTHON_REPO_DEBUG_VERBOSITY=<INFO|DEBUG|TRACE> <RULES_PYTHON_REPO_DEBUG_VERBOSITY>` 202""", 203 ), 204 # 600 is documented as default here: https://docs.bazel.build/versions/master/skylark/lib/repository_ctx.html#execute 205 "timeout": attr.int( 206 default = 600, 207 doc = "Timeout (in seconds) on the rule's execution duration.", 208 ), 209} 210 211def use_isolated(ctx, attr): 212 """Determine whether or not to pass the pip `--isolated` flag to the pip invocation. 213 214 Args: 215 ctx: repository or module context 216 attr: attributes for the repo rule or tag extension 217 218 Returns: 219 True if --isolated should be passed 220 """ 221 use_isolated = attr.isolated 222 223 # The environment variable will take precedence over the attribute 224 isolated_env = ctx.os.environ.get("RULES_PYTHON_PIP_ISOLATED", None) 225 if isolated_env != None: 226 if isolated_env.lower() in ("0", "false"): 227 use_isolated = False 228 else: 229 use_isolated = True 230 231 return use_isolated 232