Add support for `HF_TOKEN` (#14797)

## Summary

If `HF_TOKEN` is set, we'll automatically wire it up to authenticate
requests when hitting private `huggingface.co` URLs in `uv run`.

## Test Plan

An unauthenticated request:

```
> cargo run -- run https://huggingface.co/datasets/cmarsh/test/resolve/main/main.py

  File "/var/folders/nt/6gf2v7_s3k13zq_t3944rwz40000gn/T/mainYadr5M.py", line 1
    Invalid username or password.
            ^^^^^^^^
SyntaxError: invalid syntax
```

An authenticated request:

```
> HF_TOKEN=hf_... cargo run run https://huggingface.co/datasets/cmarsh/test/resolve/main/main.py

Hello from main.py!
```
This commit is contained in:
Charlie Marsh 2025-07-21 16:55:33 -04:00 committed by GitHub
parent 7a56950bab
commit a3ea1b69f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 91 additions and 3 deletions

View File

@ -15,6 +15,7 @@ mod credentials;
mod index; mod index;
mod keyring; mod keyring;
mod middleware; mod middleware;
mod providers;
mod realm; mod realm;
// TODO(zanieb): Consider passing a cache explicitly throughout // TODO(zanieb): Consider passing a cache explicitly throughout

View File

@ -7,6 +7,7 @@ use reqwest::{Request, Response};
use reqwest_middleware::{Error, Middleware, Next}; use reqwest_middleware::{Error, Middleware, Next};
use tracing::{debug, trace, warn}; use tracing::{debug, trace, warn};
use crate::providers::HuggingFaceProvider;
use crate::{ use crate::{
CREDENTIALS_CACHE, CredentialsCache, KeyringProvider, CREDENTIALS_CACHE, CredentialsCache, KeyringProvider,
cache::FetchUrl, cache::FetchUrl,
@ -457,9 +458,8 @@ impl AuthMiddleware {
Some(credentials) Some(credentials)
}; };
return self self.complete_request(credentials, request, extensions, next, auth_policy)
.complete_request(credentials, request, extensions, next, auth_policy) .await
.await;
} }
/// Fetch credentials for a URL. /// Fetch credentials for a URL.
@ -503,6 +503,13 @@ impl AuthMiddleware {
return credentials; return credentials;
} }
// Support for known providers, like Hugging Face.
if let Some(credentials) = HuggingFaceProvider::credentials_for(url).map(Arc::new) {
debug!("Found Hugging Face credentials for {url}");
self.cache().fetches.done(key, Some(credentials.clone()));
return Some(credentials);
}
// Netrc support based on: <https://github.com/gribouille/netrc>. // Netrc support based on: <https://github.com/gribouille/netrc>.
let credentials = if let Some(credentials) = self.netrc.get().and_then(|netrc| { let credentials = if let Some(credentials) = self.netrc.get().and_then(|netrc| {
debug!("Checking netrc for credentials for {url}"); debug!("Checking netrc for credentials for {url}");

View File

@ -0,0 +1,49 @@
use std::sync::LazyLock;
use tracing::debug;
use url::Url;
use uv_static::EnvVars;
use crate::Credentials;
use crate::realm::Realm;
/// The [`Realm`] for the Hugging Face platform.
static HUGGING_FACE_REALM: LazyLock<Realm> = LazyLock::new(|| {
let url = Url::parse("https://huggingface.co").expect("Failed to parse Hugging Face URL");
Realm::from(&url)
});
/// The authentication token for the Hugging Face platform, if set.
static HUGGING_FACE_TOKEN: LazyLock<Option<Vec<u8>>> = LazyLock::new(|| {
// Extract the Hugging Face token from the environment variable, if it exists.
let hf_token = std::env::var(EnvVars::HF_TOKEN)
.ok()
.map(String::into_bytes)
.filter(|token| !token.is_empty())?;
if std::env::var_os(EnvVars::UV_NO_HF_TOKEN).is_some() {
debug!("Ignoring Hugging Face token from environment due to `UV_NO_HF_TOKEN`");
return None;
}
debug!("Found Hugging Face token in environment");
Some(hf_token)
});
/// A provider for authentication credentials for the Hugging Face platform.
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct HuggingFaceProvider;
impl HuggingFaceProvider {
/// Returns the credentials for the Hugging Face platform, if available.
pub(crate) fn credentials_for(url: &Url) -> Option<Credentials> {
if Realm::from(url) == *HUGGING_FACE_REALM {
if let Some(token) = HUGGING_FACE_TOKEN.as_ref() {
return Some(Credentials::Bearer {
token: token.clone(),
});
}
}
None
}
}

View File

@ -765,4 +765,11 @@ impl EnvVars {
/// Disable GitHub-specific requests that allow uv to skip `git fetch` in some circumstances. /// Disable GitHub-specific requests that allow uv to skip `git fetch` in some circumstances.
pub const UV_NO_GITHUB_FAST_PATH: &'static str = "UV_NO_GITHUB_FAST_PATH"; pub const UV_NO_GITHUB_FAST_PATH: &'static str = "UV_NO_GITHUB_FAST_PATH";
/// Authentication token for Hugging Face requests. When set, uv will use this token
/// when making requests to `https://huggingface.co/` and any subdomains.
pub const HF_TOKEN: &'static str = "HF_TOKEN";
/// Disable Hugging Face authentication, even if `HF_TOKEN` is set.
pub const UV_NO_HF_TOKEN: &'static str = "UV_NO_HF_TOKEN";
} }

View File

@ -151,3 +151,18 @@ insecure.
Use `allow-insecure-host` with caution and only in trusted environments, as it can expose you to Use `allow-insecure-host` with caution and only in trusted environments, as it can expose you to
security risks due to the lack of certificate verification. security risks due to the lack of certificate verification.
## Hugging Face support
uv supports automatic authentication for the Hugging Face Hub. Specifically, if the `HF_TOKEN`
environment variable is set, uv will propagate it to requests to `huggingface.co`.
This is particularly useful for accessing private scripts in Hugging Face Datasets. For example, you
can run the following command to execute the script `main.py` script from a private dataset:
```console
$ HF_TOKEN=hf_... uv run https://huggingface.co/datasets/<user>/<name>/resolve/<branch>/main.py
```
You can disable automatic Hugging Face authentication by setting the `UV_NO_HF_TOKEN=1` environment
variable.

View File

@ -252,6 +252,10 @@ Ignore `.env` files when executing `uv run` commands.
Disable GitHub-specific requests that allow uv to skip `git fetch` in some circumstances. Disable GitHub-specific requests that allow uv to skip `git fetch` in some circumstances.
### `UV_NO_HF_TOKEN`
Disable Hugging Face authentication, even if `HF_TOKEN` is set.
### `UV_NO_INSTALLER_METADATA` ### `UV_NO_INSTALLER_METADATA`
Skip writing `uv` installer metadata files (e.g., `INSTALLER`, `REQUESTED`, and `direct_url.json`) to site-packages `.dist-info` directories. Skip writing `uv` installer metadata files (e.g., `INSTALLER`, `REQUESTED`, and `direct_url.json`) to site-packages `.dist-info` directories.
@ -528,6 +532,11 @@ See [force-color.org](https://force-color.org).
Used for trusted publishing via `uv publish`. Used for trusted publishing via `uv publish`.
### `HF_TOKEN`
Authentication token for Hugging Face requests. When set, uv will use this token
when making requests to `https://huggingface.co/` and any subdomains.
### `HOME` ### `HOME`
The standard `HOME` env var. The standard `HOME` env var.