Repository navigation
whl_library.BuildWheelFromSource uses pip to download dependencies and does not inject credential helper information. #2640
Description
🐞 bug report
Affected Rule
pip.parse and the underlying code
Is this a regression?
Not that I can tell.
Description
When using python package index that requires authentication, whl_library.BuildWheelFromSource will pass that index arg to the pip wheel command.
This is a problem when using the Bazel downloader (experimental_index_url et. al.). Bazel can authenticate to the package index via a credential helper header injection and can successfully download the source tarball. However, rules_python then tries to create a wheel from that tarball and uses pip wheel --index-url "${INDEX_NEEDING_AUTH}" .... Thus building the wheel fails because pip can't auth to the private index.
🔬 Minimal Reproduction
Hard to repro if you don't have a private index readily available, but the gist is:
- Use only package indexes that require authentication
- Use experimental Bazel downloader
experimental_index_url - Attempt to install a package that fits both of these requirements.
pygraphvizis a good example of such a package.- Does not have a wheel available on the package index
- Requires an additional package (such as
setuptools) in order to build a wheel.
🔥 Exception or Error
root@b4337bd118dd:/bazel_starter# time bazel build --nobuild --config=local --remote_cache= --bes_backend= //...
INFO: Repository rules_python~~pip~pypi_312_pygraphviz_sdist_8b0b9207 instantiated at:
<builtin>: in <toplevel>
Repository rule whl_library defined at:
/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~/python/private/pypi/whl_library.bzl:469:30: in <toplevel>
INFO: repository @@rules_python~~pip~pypi_312_pygraphviz_sdist_8b0b9207' used the following cache hits instead of downloading the corresponding file.
* Hash '8b0b9207954012f3b670e53b8f8f448a28d12bdbbcf69249313bd8dbe680152f' for https://REDACTED_1/pygraphviz/pygraphviz-1.12.tar.gz
If the definition of 'repository @@rules_python~~pip~pypi_312_pygraphviz_sdist_8b0b9207' was updated, verify that the hashes were also updated.
ERROR: /root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~/python/private/repo_utils.bzl:79:16: An error occurred during the fetch of repository 'rules_python~~pip~pypi_312_pygraphviz_sdist_8b0b9207':
Traceback (most recent call last):
File "/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~/python/private/pypi/whl_library.bzl", line 245, column 40, in _whl_library_impl
pypi_repo_utils.execute_checked(
File "/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~/python/private/pypi/pypi_repo_utils.bzl", line 133, column 38, in _execute_checked
return repo_utils.execute_checked(
File "/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~/python/private/repo_utils.bzl", line 216, column 29, in _execute_checked
return _execute_internal(fail_on_error = True, *args, **kwargs)
File "/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~/python/private/repo_utils.bzl", line 147, column 27, in _execute_internal
return logger.fail((
File "/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~/python/private/repo_utils.bzl", line 89, column 39, in lambda
fail = lambda message_cb: _log(-1, "FAIL", message_cb, fail),
File "/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~/python/private/repo_utils.bzl", line 79, column 16, in _log
printer("\nrules_python:{} {}:".format(
Error in fail:
rules_python:whl_library(@@rules_python~~pip~pypi_312_pygraphviz_sdist_8b0b9207) FAIL: repo.execute: whl_library.BuildWheelFromSource(rules_python~~pip~pypi_312_pygraphviz_sdist_8b0b9207, pygraphviz==1.12): end: failure:
command: /root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~python~python_3_12_2_host/python -m python.private.pypi.whl_installer.wheel_installer --requirement pygraphviz==1.12 --isolated --extra_pip_args "{\"arg\":[\"--index-url\",\"https://oauth2accesstoken@REDACTED_2/simple\",\"--extra-index-url\",\"https://oauth2accesstoken@REDACTED_1/simple\",\"--find-links\",\".\"]}" --pip_data_exclude "{\"arg\":[]}" --environment "{\"arg\":{}}"
return code: 1
working dir: <default: /root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~pip~pypi_312_pygraphviz_sdist_8b0b9207>
timeout: 600
environment:
PYTHONPATH="/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~:/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~internal_deps~pypi__build:/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~internal_deps~pypi__click:/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~internal_deps~py
pi__colorama:/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~internal_deps~pypi__importlib_metadata:/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~internal_deps~pypi__installer:/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~internal_deps~pypi__more_itertools:/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3
aa1b8af741d50/external/rules_python~~internal_deps~pypi__packaging:/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~internal_deps~pypi__pep517:/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~internal_deps~pypi__pip:/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~internal_deps~pypi__pip_tools:/root/.cache/bazel/
_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~internal_deps~pypi__pyproject_hooks:/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~internal_deps~pypi__setuptools:/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~internal_deps~pypi__tomli:/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~intern
al_deps~pypi__wheel:/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~internal_deps~pypi__zipp"
CPPFLAGS="-isystem /root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~python~python_3_12_2_host/include/python3.12"
===== stdout start =====
Looking in indexes: https://****@REDACTED_2/simple, https://****@REDACTED_1/simple Looking in links: .
Processing ./pygraphviz-1.12.tar.gz (from -r /tmp/tmp192c1dbh (line 1))
File was already downloaded /root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~pip~pypi_312_pygraphviz_sdist_8b0b9207/pygraphviz-1.12.tar.gz
Installing build dependencies: started
Installing build dependencies: finished with status 'error'
===== stdout end =====
===== stderr start =====
WARNING: 401 Error, Credentials not correct for https://REDACTED_2/simple/pygraphviz/
WARNING: 401 Error, Credentials not correct for https://REDACTED_1/simple/pygraphviz/
error: subprocess-exited-with-error
× pip subprocess to install build dependencies did not run successfully.
│ exit code: 1
╰─> [6 lines of output]
Looking in indexes: https://****@REDACTED_2/simple, https://****@REDACTED_1/simple
Looking in links: .
WARNING: 401 Error, Credentials not correct for https://REDACTED_2/simple/setuptools/
WARNING: 401 Error, Credentials not correct for https://REDACTED_1/simple/setuptools/
ERROR: Could not find a version that satisfies the requirement setuptools>=61.2 (from versions: none)
ERROR: No matching distribution found for setuptools>=61.2
[end of output]
note: This error originates from a subprocess, and is likely not a problem with pip.
error: subprocess-exited-with-error
× pip subprocess to install build dependencies did not run successfully.
│ exit code: 1
╰─> See above for output.
note: This error originates from a subprocess, and is likely not a problem with pip.
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~/python/private/pypi/whl_installer/wheel_installer.py", line 205, in <module>
main()
File "/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~/python/private/pypi/whl_installer/wheel_installer.py", line 190, in main
subprocess.run(pip_args, check=True, env=env)
File "/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~python~python_3_12_2_x86_64-unknown-linux-gnu/lib/python3.12/subprocess.py", line 571, in run
raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['/root/.cache/bazel/_bazel_root/dd66987607986e0bbc3aa1b8af741d50/external/rules_python~~python~python_3_12_2_host/python', '-m', 'pip', '--isolated', 'wheel', '--no-deps', '--index-url', 'https://oauth2accesstoken@REDACTED_2/simple', '--extra-index-url', 'https://oauth2accesstoken@REDACTED_1/simple', '--find-links', '.',
'-r', '/tmp/tmp192c1dbh']' returned non-zero exit status 1.
===== stderr end =====
🌍 Your Environment
Operating System:
gLinux (based on Debian testing)
Output of bazel version:
$ bazel version
Bazelisk version: v1.20.0
Starting local Bazel server and connecting to it...
Build label: 7.4.1
Build target: @@//src/main/java/com/google/devtools/build/lib/bazel:BazelServer
Build time: Mon Nov 11 21:24:53 2024 (1731360293)
Build timestamp: 1731360293
Build timestamp as int: 1731360293
Rules_python version:
1.1.0
Anything else relevant?
Internal bug: b/399782261
I think I see a couple potential paths forward:
- support keyring when building wheels
- At least with some private registries, they support using
keyringas an auth provider. This may allow the internalpip wheelcommand to auth to the registry.
- At least with some private registries, they support using
- support using the Bazel downloader, and thus the credential helper, when building wheels
- Manually replacing
pip wheel's downloading action with a separate Bazel download task might work, but only if people are using the Bazel downloader
- Manually replacing
- support passing a secret value (the access token) down to the pip wheel command and injecting that secret into the (extra) index url.
- As far as I know, all package indexes support auth via URL credential injection a-la
https://oauth2accesstoken:${SECRET}@private_index.com/simple. It might be possible to add an attribute topip.parsethat injects that secret.
- As far as I know, all package indexes support auth via URL credential injection a-la
Activity
aignas commentedon Feb 28, 2025
@dougthor42, I think you had some experiments with adding more deps to the pip parse to include credetial support, but that was somewhat hard to complete.
Related, I think we should focus on #2410 so that we don't have a need to extend the pip dependencies to support credentials.
Potentially related: #2328, where at some point we removed the
--no-index-urlfrom the code branch in question.refactor: Add log_std(out|err) bools to repo_utils that execute a sub…
dougthor42 commentedon May 1, 2025
FYI: I'm currently working around this with the following patch, with some things redacted. Hopefully I didn't accidentally remove any lines in the copy-paste-redact or else the patch line numbers will be all mucked up 🙃.
rickeylev commentedon May 1, 2025
Have you tried a netrc file? https://pip.pypa.io/en/stable/topics/authentication/#netrc-support
pip.parse has a netrc arg: https://rules-python.readthedocs.io/en/latest/api/rules_python/python/extensions/pip.html#pip.parse.netrc
aignas commentedon May 2, 2025
FYI,
~/.netrcis picked up by default.dougthor42 commentedon May 5, 2025
.netrc... that's a name I haven't heard in a long time. So long that I forgot about it!I did some testing (thanks to @binarystardust for actually doing the testing - I just copied his work 🙃) and it does indeed work. Now I just have to verify with our security team that using
.netrcis OK, as it does still store plaintext secrets.tkinz27 commentedon Jul 11, 2025
Its not quite hermetic, but I get this to work with by adding
to our
pip.parsemethod and then ensuring our developers have keyring installed in their PATH. We are using Google Artifact Registry. My whole pip.parse looks likesdistin a build action #2410aignas commentedon Oct 14, 2025
In the long term #2410 is the real solution, so just linking the issues together.
dougthor42 commentedon Jun 19, 2026
This appears to no longer be an issue in 2.0 (see #3826 (comment)). I'm assuming that #3657 resolved it.