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