mirror of https://github.com/astral-sh/uv
Deny some fields from the `pyproject.toml`
This commit is contained in:
parent
0daef692df
commit
47badd6f8b
|
|
@ -283,6 +283,19 @@ fn emit_field(output: &mut String, name: &str, field: &OptionField, parents: &[S
|
|||
option_type: OptionType::Configuration,
|
||||
..
|
||||
} => {
|
||||
if field.uv_toml_only {
|
||||
// Only show uv.toml example for fields that are not allowed in pyproject.toml
|
||||
output.push_str(&format_code(
|
||||
"uv.toml",
|
||||
&format_header(
|
||||
field.scope,
|
||||
field.example,
|
||||
parents,
|
||||
ConfigurationFile::UvToml,
|
||||
),
|
||||
field.example,
|
||||
));
|
||||
} else {
|
||||
output.push_str(&format_tab(
|
||||
"pyproject.toml",
|
||||
&format_header(
|
||||
|
|
@ -304,6 +317,7 @@ fn emit_field(output: &mut String, name: &str, field: &OptionField, parents: &[S
|
|||
field.example,
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
output.push('\n');
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ fn handle_option(field: &Field, attr: &Attribute) -> syn::Result<TokenStream> {
|
|||
example,
|
||||
scope,
|
||||
possible_values,
|
||||
uv_toml_only,
|
||||
} = parse_field_attributes(attr)?;
|
||||
let kebab_name = LitStr::new(&ident.to_string().replace('_', "-"), ident.span());
|
||||
|
||||
|
|
@ -244,6 +245,8 @@ fn handle_option(field: &Field, attr: &Attribute) -> syn::Result<TokenStream> {
|
|||
quote!(None)
|
||||
};
|
||||
|
||||
let uv_toml_only = uv_toml_only.unwrap_or(false);
|
||||
|
||||
Ok(quote_spanned!(
|
||||
ident.span() => {
|
||||
visit.record_field(#kebab_name, uv_options_metadata::OptionField{
|
||||
|
|
@ -254,6 +257,7 @@ fn handle_option(field: &Field, attr: &Attribute) -> syn::Result<TokenStream> {
|
|||
scope: #scope,
|
||||
deprecated: #deprecated,
|
||||
possible_values: #possible_values,
|
||||
uv_toml_only: #uv_toml_only,
|
||||
})
|
||||
}
|
||||
))
|
||||
|
|
@ -266,6 +270,7 @@ struct FieldAttributes {
|
|||
example: String,
|
||||
scope: Option<String>,
|
||||
possible_values: Option<bool>,
|
||||
uv_toml_only: Option<bool>,
|
||||
}
|
||||
|
||||
fn parse_field_attributes(attribute: &Attribute) -> syn::Result<FieldAttributes> {
|
||||
|
|
@ -274,6 +279,7 @@ fn parse_field_attributes(attribute: &Attribute) -> syn::Result<FieldAttributes>
|
|||
let mut example = None;
|
||||
let mut scope = None;
|
||||
let mut possible_values = None;
|
||||
let mut uv_toml_only = None;
|
||||
|
||||
attribute.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("default") {
|
||||
|
|
@ -287,6 +293,8 @@ fn parse_field_attributes(attribute: &Attribute) -> syn::Result<FieldAttributes>
|
|||
example = Some(dedent(&example_text).trim_matches('\n').to_string());
|
||||
} else if meta.path.is_ident("possible_values") {
|
||||
possible_values = get_bool_literal(&meta, "possible_values", "option")?;
|
||||
} else if meta.path.is_ident("uv_toml_only") {
|
||||
uv_toml_only = get_bool_literal(&meta, "uv_toml_only", "option")?;
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
meta.path.span(),
|
||||
|
|
@ -327,6 +335,7 @@ fn parse_field_attributes(attribute: &Attribute) -> syn::Result<FieldAttributes>
|
|||
example,
|
||||
scope,
|
||||
possible_values,
|
||||
uv_toml_only,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -258,6 +258,8 @@ pub struct OptionField {
|
|||
pub example: &'static str,
|
||||
pub deprecated: Option<Deprecated>,
|
||||
pub possible_values: Option<Vec<PossibleValue>>,
|
||||
/// If `true`, the option is only available in `uv.toml`, not `pyproject.toml`.
|
||||
pub uv_toml_only: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
|
||||
|
|
@ -343,6 +345,7 @@ mod tests {
|
|||
scope: None,
|
||||
deprecated: None,
|
||||
possible_values: None,
|
||||
uv_toml_only: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -368,6 +371,7 @@ mod tests {
|
|||
scope: None,
|
||||
deprecated: None,
|
||||
possible_values: None,
|
||||
uv_toml_only: false,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -389,6 +393,7 @@ mod tests {
|
|||
scope: None,
|
||||
deprecated: None,
|
||||
possible_values: None,
|
||||
uv_toml_only: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -411,6 +416,7 @@ mod tests {
|
|||
scope: None,
|
||||
deprecated: None,
|
||||
possible_values: None,
|
||||
uv_toml_only: false,
|
||||
};
|
||||
|
||||
impl OptionsMetadata for WithOptions {
|
||||
|
|
@ -436,6 +442,7 @@ mod tests {
|
|||
scope: None,
|
||||
deprecated: None,
|
||||
possible_values: None,
|
||||
uv_toml_only: false,
|
||||
};
|
||||
|
||||
struct Root;
|
||||
|
|
@ -452,6 +459,7 @@ mod tests {
|
|||
scope: None,
|
||||
deprecated: None,
|
||||
possible_values: None,
|
||||
uv_toml_only: false,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ impl FilesystemOptions {
|
|||
let options = options.relative_to(&std::path::absolute(dir)?)?;
|
||||
|
||||
tracing::debug!("Found workspace configuration at `{}`", path.display());
|
||||
validate_pyproject_toml(&path, &options)?;
|
||||
return Ok(Some(Self(options)));
|
||||
}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {}
|
||||
|
|
@ -284,6 +285,83 @@ fn validate_uv_toml(path: &Path, options: &Options) -> Result<(), Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate that an [`Options`] schema is compatible with `pyproject.toml`.
|
||||
///
|
||||
/// Some settings are user-specific and should not be in a `pyproject.toml` file
|
||||
/// that may be checked into version control.
|
||||
fn validate_pyproject_toml(path: &Path, options: &Options) -> Result<(), Error> {
|
||||
let Options {
|
||||
globals:
|
||||
GlobalOptions {
|
||||
required_version: _,
|
||||
native_tls,
|
||||
offline,
|
||||
no_cache,
|
||||
cache_dir,
|
||||
preview: _,
|
||||
python_preference: _,
|
||||
python_downloads: _,
|
||||
concurrent_downloads: _,
|
||||
concurrent_builds: _,
|
||||
concurrent_installs: _,
|
||||
http_proxy,
|
||||
https_proxy,
|
||||
no_proxy,
|
||||
allow_insecure_host,
|
||||
},
|
||||
top_level: _,
|
||||
install_mirrors: _,
|
||||
publish: _,
|
||||
add: _,
|
||||
pip: _,
|
||||
cache_keys: _,
|
||||
override_dependencies: _,
|
||||
exclude_dependencies: _,
|
||||
constraint_dependencies: _,
|
||||
build_constraint_dependencies: _,
|
||||
environments: _,
|
||||
required_environments: _,
|
||||
conflicts: _,
|
||||
workspace: _,
|
||||
sources: _,
|
||||
dev_dependencies: _,
|
||||
default_groups: _,
|
||||
dependency_groups: _,
|
||||
managed: _,
|
||||
package: _,
|
||||
build_backend: _,
|
||||
} = options;
|
||||
|
||||
if native_tls.is_some() {
|
||||
return Err(Error::UvTomlOnlyField(path.to_path_buf(), "native-tls"));
|
||||
}
|
||||
if offline.is_some() {
|
||||
return Err(Error::UvTomlOnlyField(path.to_path_buf(), "offline"));
|
||||
}
|
||||
if no_cache.is_some() {
|
||||
return Err(Error::UvTomlOnlyField(path.to_path_buf(), "no-cache"));
|
||||
}
|
||||
if cache_dir.is_some() {
|
||||
return Err(Error::UvTomlOnlyField(path.to_path_buf(), "cache-dir"));
|
||||
}
|
||||
if allow_insecure_host.is_some() {
|
||||
return Err(Error::UvTomlOnlyField(
|
||||
path.to_path_buf(),
|
||||
"allow-insecure-host",
|
||||
));
|
||||
}
|
||||
if http_proxy.is_some() {
|
||||
return Err(Error::UvTomlOnlyField(path.to_path_buf(), "http-proxy"));
|
||||
}
|
||||
if https_proxy.is_some() {
|
||||
return Err(Error::UvTomlOnlyField(path.to_path_buf(), "https-proxy"));
|
||||
}
|
||||
if no_proxy.is_some() {
|
||||
return Err(Error::UvTomlOnlyField(path.to_path_buf(), "no-proxy"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate that an [`Options`] contains no fields that `uv.toml` would mask
|
||||
///
|
||||
/// This is essentially the inverse of [`validated_uv_toml`][].
|
||||
|
|
@ -575,6 +653,10 @@ pub enum Error {
|
|||
)]
|
||||
PyprojectOnlyField(PathBuf, &'static str),
|
||||
|
||||
#[error("Failed to parse: `{}`. The `{}` field is not allowed in a `pyproject.toml` file. `{}` is user-specific and should be placed in a `uv.toml` file, set via environment variable, or passed via the CLI instead.", _0.user_display(), _1, _1
|
||||
)]
|
||||
UvTomlOnlyField(PathBuf, &'static str),
|
||||
|
||||
#[error("Failed to parse environment variable `{name}` with invalid value `{value}`: {err}")]
|
||||
InvalidEnvironmentVariable {
|
||||
name: String,
|
||||
|
|
|
|||
|
|
@ -214,7 +214,8 @@ pub struct GlobalOptions {
|
|||
value_type = "bool",
|
||||
example = r#"
|
||||
native-tls = true
|
||||
"#
|
||||
"#,
|
||||
uv_toml_only = true
|
||||
)]
|
||||
pub native_tls: Option<bool>,
|
||||
/// Disable network access, relying only on locally cached data and locally available files.
|
||||
|
|
@ -223,7 +224,8 @@ pub struct GlobalOptions {
|
|||
value_type = "bool",
|
||||
example = r#"
|
||||
offline = true
|
||||
"#
|
||||
"#,
|
||||
uv_toml_only = true
|
||||
)]
|
||||
pub offline: Option<bool>,
|
||||
/// Avoid reading from or writing to the cache, instead using a temporary directory for the
|
||||
|
|
@ -233,7 +235,8 @@ pub struct GlobalOptions {
|
|||
value_type = "bool",
|
||||
example = r#"
|
||||
no-cache = true
|
||||
"#
|
||||
"#,
|
||||
uv_toml_only = true
|
||||
)]
|
||||
pub no_cache: Option<bool>,
|
||||
/// Path to the cache directory.
|
||||
|
|
@ -245,7 +248,8 @@ pub struct GlobalOptions {
|
|||
value_type = "str",
|
||||
example = r#"
|
||||
cache-dir = "./.uv_cache"
|
||||
"#
|
||||
"#,
|
||||
uv_toml_only = true
|
||||
)]
|
||||
pub cache_dir: Option<PathBuf>,
|
||||
/// Whether to enable experimental, preview features.
|
||||
|
|
@ -317,7 +321,8 @@ pub struct GlobalOptions {
|
|||
value_type = "str",
|
||||
example = r#"
|
||||
http-proxy = "http://proxy.example.com"
|
||||
"#
|
||||
"#,
|
||||
uv_toml_only = true
|
||||
)]
|
||||
pub http_proxy: Option<String>,
|
||||
/// The URL of the HTTPS proxy to use.
|
||||
|
|
@ -326,7 +331,8 @@ pub struct GlobalOptions {
|
|||
value_type = "str",
|
||||
example = r#"
|
||||
https-proxy = "https://proxy.example.com"
|
||||
"#
|
||||
"#,
|
||||
uv_toml_only = true
|
||||
)]
|
||||
pub https_proxy: Option<String>,
|
||||
/// A list of hosts to exclude from proxying.
|
||||
|
|
@ -335,7 +341,8 @@ pub struct GlobalOptions {
|
|||
value_type = "list[str]",
|
||||
example = r#"
|
||||
no-proxy = ["localhost", "127.0.0.1"]
|
||||
"#
|
||||
"#,
|
||||
uv_toml_only = true
|
||||
)]
|
||||
pub no_proxy: Option<Vec<String>>,
|
||||
/// Allow insecure connections to host.
|
||||
|
|
@ -351,7 +358,8 @@ pub struct GlobalOptions {
|
|||
value_type = "list[str]",
|
||||
example = r#"
|
||||
allow-insecure-host = ["localhost:8080"]
|
||||
"#
|
||||
"#,
|
||||
uv_toml_only = true
|
||||
)]
|
||||
pub allow_insecure_host: Option<Vec<TrustedHost>>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -766,15 +766,8 @@ bypasses SSL verification and could expose you to MITM attacks.
|
|||
|
||||
**Example usage**:
|
||||
|
||||
=== "pyproject.toml"
|
||||
```toml title="uv.toml"
|
||||
|
||||
```toml
|
||||
[tool.uv]
|
||||
allow-insecure-host = ["localhost:8080"]
|
||||
```
|
||||
=== "uv.toml"
|
||||
|
||||
```toml
|
||||
allow-insecure-host = ["localhost:8080"]
|
||||
```
|
||||
|
||||
|
|
@ -793,15 +786,8 @@ Defaults to `$XDG_CACHE_HOME/uv` or `$HOME/.cache/uv` on Linux and macOS, and
|
|||
|
||||
**Example usage**:
|
||||
|
||||
=== "pyproject.toml"
|
||||
```toml title="uv.toml"
|
||||
|
||||
```toml
|
||||
[tool.uv]
|
||||
cache-dir = "./.uv_cache"
|
||||
```
|
||||
=== "uv.toml"
|
||||
|
||||
```toml
|
||||
cache-dir = "./.uv_cache"
|
||||
```
|
||||
|
||||
|
|
@ -1326,15 +1312,8 @@ The URL of the HTTP proxy to use.
|
|||
|
||||
**Example usage**:
|
||||
|
||||
=== "pyproject.toml"
|
||||
```toml title="uv.toml"
|
||||
|
||||
```toml
|
||||
[tool.uv]
|
||||
http-proxy = "http://proxy.example.com"
|
||||
```
|
||||
=== "uv.toml"
|
||||
|
||||
```toml
|
||||
http-proxy = "http://proxy.example.com"
|
||||
```
|
||||
|
||||
|
|
@ -1350,15 +1329,8 @@ The URL of the HTTPS proxy to use.
|
|||
|
||||
**Example usage**:
|
||||
|
||||
=== "pyproject.toml"
|
||||
```toml title="uv.toml"
|
||||
|
||||
```toml
|
||||
[tool.uv]
|
||||
https-proxy = "https://proxy.example.com"
|
||||
```
|
||||
=== "uv.toml"
|
||||
|
||||
```toml
|
||||
https-proxy = "https://proxy.example.com"
|
||||
```
|
||||
|
||||
|
|
@ -1564,15 +1536,8 @@ included in your system's certificate store.
|
|||
|
||||
**Example usage**:
|
||||
|
||||
=== "pyproject.toml"
|
||||
```toml title="uv.toml"
|
||||
|
||||
```toml
|
||||
[tool.uv]
|
||||
native-tls = true
|
||||
```
|
||||
=== "uv.toml"
|
||||
|
||||
```toml
|
||||
native-tls = true
|
||||
```
|
||||
|
||||
|
|
@ -1746,15 +1711,8 @@ duration of the operation.
|
|||
|
||||
**Example usage**:
|
||||
|
||||
=== "pyproject.toml"
|
||||
```toml title="uv.toml"
|
||||
|
||||
```toml
|
||||
[tool.uv]
|
||||
no-cache = true
|
||||
```
|
||||
=== "uv.toml"
|
||||
|
||||
```toml
|
||||
no-cache = true
|
||||
```
|
||||
|
||||
|
|
@ -1795,15 +1753,8 @@ A list of hosts to exclude from proxying.
|
|||
|
||||
**Example usage**:
|
||||
|
||||
=== "pyproject.toml"
|
||||
```toml title="uv.toml"
|
||||
|
||||
```toml
|
||||
[tool.uv]
|
||||
no-proxy = ["localhost", "127.0.0.1"]
|
||||
```
|
||||
=== "uv.toml"
|
||||
|
||||
```toml
|
||||
no-proxy = ["localhost", "127.0.0.1"]
|
||||
```
|
||||
|
||||
|
|
@ -1845,15 +1796,8 @@ Disable network access, relying only on locally cached data and locally availabl
|
|||
|
||||
**Example usage**:
|
||||
|
||||
=== "pyproject.toml"
|
||||
```toml title="uv.toml"
|
||||
|
||||
```toml
|
||||
[tool.uv]
|
||||
offline = true
|
||||
```
|
||||
=== "uv.toml"
|
||||
|
||||
```toml
|
||||
offline = true
|
||||
```
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue