| # Copyright 2023 The Bazel Authors. All rights reserved. | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| "common attributes for whl_library and pip_repository" | |
| ATTRS = { | |
| "download_only": attr.bool( | |
| doc = """ | |
| Whether to use "pip download" instead of "pip wheel". Disables building wheels from source, but allows use of | |
| --platform, --python-version, --implementation, and --abi in --extra_pip_args to download wheels for a different | |
| platform from the host platform. | |
| """, | |
| ), | |
| "enable_implicit_namespace_pkgs": attr.bool( | |
| default = False, | |
| doc = """ | |
| If true, disables conversion of native namespace packages into pkg-util style namespace packages. When set all py_binary | |
| and py_test targets must specify either `legacy_create_init=False` or the global Bazel option | |
| `--incompatible_default_to_explicit_init_py` to prevent `__init__.py` being automatically generated in every directory. | |
| This option is required to support some packages which cannot handle the conversion to pkg-util style. | |
| """, | |
| ), | |
| "environment": attr.string_dict( | |
| doc = """ | |
| Environment variables to set in the pip subprocess. | |
| Can be used to set common variables such as `http_proxy`, `https_proxy` and `no_proxy` | |
| Note that pip is run with "--isolated" on the CLI so `PIP_<VAR>_<NAME>` | |
| style env vars are ignored, but env vars that control requests and urllib3 | |
| can be passed. If you need `PIP_<VAR>_<NAME>`, take a look at `extra_pip_args` | |
| and `envsubst`. | |
| """, | |
| default = {}, | |
| ), | |
| "envsubst": attr.string_list( | |
| mandatory = False, | |
| doc = """\ | |
| A list of environment variables to substitute (e.g. `["PIP_INDEX_URL", | |
| "PIP_RETRIES"]`). The corresponding variables are expanded in `extra_pip_args` | |
| using the syntax `$VARNAME` or `${VARNAME}` (expanding to empty string if unset) | |
| or `${VARNAME:-default}` (expanding to default if the variable is unset or empty | |
| in the environment). Note: On Bazel 6 and Bazel 7.0 changes to the variables named | |
| here do not cause packages to be re-fetched. Don't fetch different things based | |
| on the value of these variables. | |
| """, | |
| ), | |
| "experimental_requirement_cycles": attr.string_list_dict( | |
| default = {}, | |
| doc = """\ | |
| A mapping of dependency cycle names to a list of requirements which form that cycle. | |
| Requirements which form cycles will be installed together and taken as | |
| dependencies together in order to ensure that the cycle is always satisified. | |
| Example: | |
| `sphinx` depends on `sphinxcontrib-serializinghtml` | |
| When listing both as requirements, ala | |
| ``` | |
| py_binary( | |
| name = "doctool", | |
| ... | |
| deps = [ | |
| "@pypi//sphinx:pkg", | |
| "@pypi//sphinxcontrib_serializinghtml", | |
| ] | |
| ) | |
| ``` | |
| Will produce a Bazel error such as | |
| ``` | |
| ERROR: .../external/pypi_sphinxcontrib_serializinghtml/BUILD.bazel:44:6: in alias rule @pypi_sphinxcontrib_serializinghtml//:pkg: cycle in dependency graph: | |
| //:doctool (...) | |
| @pypi//sphinxcontrib_serializinghtml:pkg (...) | |
| .-> @pypi_sphinxcontrib_serializinghtml//:pkg (...) | |
| | @pypi_sphinxcontrib_serializinghtml//:_pkg (...) | |
| | @pypi_sphinx//:pkg (...) | |
| | @pypi_sphinx//:_pkg (...) | |
| `-- @pypi_sphinxcontrib_serializinghtml//:pkg (...) | |
| ``` | |
| Which we can resolve by configuring these two requirements to be installed together as a cycle | |
| ``` | |
| pip_parse( | |
| ... | |
| experimental_requirement_cycles = { | |
| "sphinx": [ | |
| "sphinx", | |
| "sphinxcontrib-serializinghtml", | |
| ] | |
| }, | |
| ) | |
| ``` | |
| Warning: | |
| If a dependency participates in multiple cycles, all of those cycles must be | |
| collapsed down to one. For instance `a <-> b` and `a <-> c` cannot be listed | |
| as two separate cycles. | |
| """, | |
| ), | |
| "experimental_target_platforms": attr.string_list( | |
| default = [], | |
| doc = """\ | |
| A list of platforms that we will generate the conditional dependency graph for | |
| cross platform wheels by parsing the wheel metadata. This will generate the | |
| correct dependencies for packages like `sphinx` or `pylint`, which include | |
| `colorama` when installed and used on Windows platforms. | |
| An empty list means falling back to the legacy behaviour where the host | |
| platform is the target platform. | |
| WARNING: It may not work as expected in cases where the python interpreter | |
| implementation that is being used at runtime is different between different platforms. | |
| This has been tested for CPython only. | |
| For specific target platforms use values of the form `<os>_<arch>` where `<os>` | |
| is one of `linux`, `osx`, `windows` and arch is one of `x86_64`, `x86_32`, | |
| `aarch64`, `s390x` and `ppc64le`. | |
| You can also target a specific Python version by using `cp3<minor_version>_<os>_<arch>`. | |
| If multiple python versions are specified as target platforms, then select statements | |
| of the `lib` and `whl` targets will include usage of version aware toolchain config | |
| settings like `@rules_python//python/config_settings:is_python_3.y`. | |
| Special values: `host` (for generating deps for the host platform only) and | |
| `<prefix>_*` values. For example, `cp39_*`, `linux_*`, `cp39_linux_*`. | |
| NOTE: this is not for cross-compiling Python wheels but rather for parsing the `whl` METADATA correctly. | |
| """, | |
| ), | |
| "extra_pip_args": attr.string_list( | |
| doc = """Extra arguments to pass on to pip. Must not contain spaces. | |
| Supports environment variables using the syntax `$VARNAME` or | |
| `${VARNAME}` (expanding to empty string if unset) or | |
| `${VARNAME:-default}` (expanding to default if the variable is unset | |
| or empty in the environment), if `"VARNAME"` is listed in the | |
| `envsubst` attribute. See also `envsubst`. | |
| """, | |
| ), | |
| "isolated": attr.bool( | |
| doc = """\ | |
| Whether or not to pass the [--isolated](https://pip.pypa.io/en/stable/cli/pip/#cmdoption-isolated) flag to | |
| the underlying pip command. Alternatively, the `RULES_PYTHON_PIP_ISOLATED` environment variable can be used | |
| to control this flag. | |
| """, | |
| default = True, | |
| ), | |
| "pip_data_exclude": attr.string_list( | |
| doc = "Additional data exclusion parameters to add to the pip packages BUILD file.", | |
| ), | |
| "python_interpreter": attr.string( | |
| doc = """\ | |
| The python interpreter to use. This can either be an absolute path or the name | |
| of a binary found on the host's `PATH` environment variable. If no value is set | |
| `python3` is defaulted for Unix systems and `python.exe` for Windows. | |
| """, | |
| # NOTE: This attribute should not have a default. See `_get_python_interpreter_attr` | |
| # default = "python3" | |
| ), | |
| "python_interpreter_target": attr.label( | |
| allow_single_file = True, | |
| doc = """ | |
| If you are using a custom python interpreter built by another repository rule, | |
| use this attribute to specify its BUILD target. This allows pip_repository to invoke | |
| pip using the same interpreter as your toolchain. If set, takes precedence over | |
| python_interpreter. An example value: "@python3_x86_64-unknown-linux-gnu//:python". | |
| """, | |
| ), | |
| "quiet": attr.bool( | |
| default = True, | |
| doc = """\ | |
| If True, suppress printing stdout and stderr output to the terminal. | |
| If you would like to get more diagnostic output, please use: | |
| RULES_PYTHON_REPO_DEBUG=1 | |
| or | |
| RULES_PYTHON_REPO_DEBUG_VERBOSITY=<INFO|DEBUG|TRACE> | |
| """, | |
| ), | |
| # 600 is documented as default here: https://docs.bazel.build/versions/master/skylark/lib/repository_ctx.html#execute | |
| "timeout": attr.int( | |
| default = 600, | |
| doc = "Timeout (in seconds) on the rule's execution duration.", | |
| ), | |
| } | |
| def use_isolated(ctx, attr): | |
| """Determine whether or not to pass the pip `--isolated` flag to the pip invocation. | |
| Args: | |
| ctx: repository or module context | |
| attr: attributes for the repo rule or tag extension | |
| Returns: | |
| True if --isolated should be passed | |
| """ | |
| use_isolated = attr.isolated | |
| # The environment variable will take precedence over the attribute | |
| isolated_env = ctx.os.environ.get("RULES_PYTHON_PIP_ISOLATED", None) | |
| if isolated_env != None: | |
| if isolated_env.lower() in ("0", "false"): | |
| use_isolated = False | |
| else: | |
| use_isolated = True | |
| return use_isolated |