mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-26 12:09:12 +00:00
Add support for config_settings
in PEP 517 hooks (#1833)
## Summary Adds `--config-setting` / `-C` (with a `--config-settings` alias for convenience) to the CLI. Closes https://github.com/astral-sh/uv/issues/1460.
This commit is contained in:
parent
1103298e6c
commit
aa73a4f0ea
19 changed files with 392 additions and 35 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -4600,9 +4600,12 @@ name = "uv-traits"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"clap",
|
||||||
"distribution-types",
|
"distribution-types",
|
||||||
"once-map",
|
"once-map",
|
||||||
"pep508_rs",
|
"pep508_rs",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"uv-cache",
|
"uv-cache",
|
||||||
"uv-interpreter",
|
"uv-interpreter",
|
||||||
|
|
|
@ -21,7 +21,7 @@ platform-host = { path = "../platform-host" }
|
||||||
uv-extract = { path = "../uv-extract" }
|
uv-extract = { path = "../uv-extract" }
|
||||||
uv-fs = { path = "../uv-fs" }
|
uv-fs = { path = "../uv-fs" }
|
||||||
uv-interpreter = { path = "../uv-interpreter" }
|
uv-interpreter = { path = "../uv-interpreter" }
|
||||||
uv-traits = { path = "../uv-traits" }
|
uv-traits = { path = "../uv-traits", features = ["serde"] }
|
||||||
pypi-types = { path = "../pypi-types" }
|
pypi-types = { path = "../pypi-types" }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
|
|
@ -29,7 +29,7 @@ use distribution_types::Resolution;
|
||||||
use pep508_rs::Requirement;
|
use pep508_rs::Requirement;
|
||||||
use uv_fs::Normalized;
|
use uv_fs::Normalized;
|
||||||
use uv_interpreter::{Interpreter, Virtualenv};
|
use uv_interpreter::{Interpreter, Virtualenv};
|
||||||
use uv_traits::{BuildContext, BuildKind, SetupPyStrategy, SourceBuildTrait};
|
use uv_traits::{BuildContext, BuildKind, ConfigSettings, SetupPyStrategy, SourceBuildTrait};
|
||||||
|
|
||||||
/// e.g. `pygraphviz/graphviz_wrap.c:3020:10: fatal error: graphviz/cgraph.h: No such file or directory`
|
/// e.g. `pygraphviz/graphviz_wrap.c:3020:10: fatal error: graphviz/cgraph.h: No such file or directory`
|
||||||
static MISSING_HEADER_RE: Lazy<Regex> = Lazy::new(|| {
|
static MISSING_HEADER_RE: Lazy<Regex> = Lazy::new(|| {
|
||||||
|
@ -247,6 +247,7 @@ pub struct SourceBuildContext {
|
||||||
pub struct SourceBuild {
|
pub struct SourceBuild {
|
||||||
temp_dir: TempDir,
|
temp_dir: TempDir,
|
||||||
source_tree: PathBuf,
|
source_tree: PathBuf,
|
||||||
|
config_settings: ConfigSettings,
|
||||||
/// If performing a PEP 517 build, the backend to use.
|
/// If performing a PEP 517 build, the backend to use.
|
||||||
pep517_backend: Option<Pep517Backend>,
|
pep517_backend: Option<Pep517Backend>,
|
||||||
/// The virtual environment in which to build the source distribution.
|
/// The virtual environment in which to build the source distribution.
|
||||||
|
@ -281,6 +282,7 @@ impl SourceBuild {
|
||||||
source_build_context: SourceBuildContext,
|
source_build_context: SourceBuildContext,
|
||||||
package_id: String,
|
package_id: String,
|
||||||
setup_py: SetupPyStrategy,
|
setup_py: SetupPyStrategy,
|
||||||
|
config_settings: ConfigSettings,
|
||||||
build_kind: BuildKind,
|
build_kind: BuildKind,
|
||||||
) -> Result<SourceBuild, Error> {
|
) -> Result<SourceBuild, Error> {
|
||||||
let temp_dir = tempdir_in(build_context.cache().root())?;
|
let temp_dir = tempdir_in(build_context.cache().root())?;
|
||||||
|
@ -354,6 +356,7 @@ impl SourceBuild {
|
||||||
build_context,
|
build_context,
|
||||||
&package_id,
|
&package_id,
|
||||||
build_kind,
|
build_kind,
|
||||||
|
&config_settings,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
@ -364,6 +367,7 @@ impl SourceBuild {
|
||||||
pep517_backend,
|
pep517_backend,
|
||||||
venv,
|
venv,
|
||||||
build_kind,
|
build_kind,
|
||||||
|
config_settings,
|
||||||
metadata_directory: None,
|
metadata_directory: None,
|
||||||
package_id,
|
package_id,
|
||||||
})
|
})
|
||||||
|
@ -492,10 +496,13 @@ impl SourceBuild {
|
||||||
|
|
||||||
prepare_metadata_for_build_wheel = getattr(backend, "prepare_metadata_for_build_wheel", None)
|
prepare_metadata_for_build_wheel = getattr(backend, "prepare_metadata_for_build_wheel", None)
|
||||||
if prepare_metadata_for_build_wheel:
|
if prepare_metadata_for_build_wheel:
|
||||||
print(prepare_metadata_for_build_wheel("{}"))
|
print(prepare_metadata_for_build_wheel("{}", config_settings={}))
|
||||||
else:
|
else:
|
||||||
print()
|
print()
|
||||||
"#, pep517_backend.backend_import(), escape_path_for_python(&metadata_directory)
|
"#,
|
||||||
|
pep517_backend.backend_import(),
|
||||||
|
escape_path_for_python(&metadata_directory),
|
||||||
|
self.config_settings.escape_for_python(),
|
||||||
};
|
};
|
||||||
let span = info_span!(
|
let span = info_span!(
|
||||||
"run_python_script",
|
"run_python_script",
|
||||||
|
@ -619,8 +626,13 @@ impl SourceBuild {
|
||||||
let escaped_wheel_dir = escape_path_for_python(wheel_dir);
|
let escaped_wheel_dir = escape_path_for_python(wheel_dir);
|
||||||
let script = formatdoc! {
|
let script = formatdoc! {
|
||||||
r#"{}
|
r#"{}
|
||||||
print(backend.build_{}("{}", metadata_directory={}))
|
print(backend.build_{}("{}", metadata_directory={}, config_settings={}))
|
||||||
"#, pep517_backend.backend_import(), self.build_kind, escaped_wheel_dir, metadata_directory
|
"#,
|
||||||
|
pep517_backend.backend_import(),
|
||||||
|
self.build_kind,
|
||||||
|
escaped_wheel_dir,
|
||||||
|
metadata_directory,
|
||||||
|
self.config_settings.escape_for_python()
|
||||||
};
|
};
|
||||||
let span = info_span!(
|
let span = info_span!(
|
||||||
"run_python_script",
|
"run_python_script",
|
||||||
|
@ -682,6 +694,7 @@ async fn create_pep517_build_environment(
|
||||||
build_context: &impl BuildContext,
|
build_context: &impl BuildContext,
|
||||||
package_id: &str,
|
package_id: &str,
|
||||||
build_kind: BuildKind,
|
build_kind: BuildKind,
|
||||||
|
config_settings: &ConfigSettings,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
debug!(
|
debug!(
|
||||||
"Calling `{}.get_requires_for_build_{}()`",
|
"Calling `{}.get_requires_for_build_{}()`",
|
||||||
|
@ -694,11 +707,11 @@ async fn create_pep517_build_environment(
|
||||||
|
|
||||||
get_requires_for_build = getattr(backend, "get_requires_for_build_{}", None)
|
get_requires_for_build = getattr(backend, "get_requires_for_build_{}", None)
|
||||||
if get_requires_for_build:
|
if get_requires_for_build:
|
||||||
requires = get_requires_for_build()
|
requires = get_requires_for_build(config_settings={})
|
||||||
else:
|
else:
|
||||||
requires = []
|
requires = []
|
||||||
print(json.dumps(requires))
|
print(json.dumps(requires))
|
||||||
"#, pep517_backend.backend_import(), build_kind
|
"#, pep517_backend.backend_import(), build_kind, config_settings.escape_for_python()
|
||||||
};
|
};
|
||||||
let span = info_span!(
|
let span = info_span!(
|
||||||
"run_python_script",
|
"run_python_script",
|
||||||
|
|
|
@ -14,7 +14,7 @@ use uv_dispatch::BuildDispatch;
|
||||||
use uv_installer::NoBinary;
|
use uv_installer::NoBinary;
|
||||||
use uv_interpreter::Virtualenv;
|
use uv_interpreter::Virtualenv;
|
||||||
use uv_resolver::InMemoryIndex;
|
use uv_resolver::InMemoryIndex;
|
||||||
use uv_traits::{BuildContext, BuildKind, InFlight, NoBuild, SetupPyStrategy};
|
use uv_traits::{BuildContext, BuildKind, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
pub(crate) struct BuildArgs {
|
pub(crate) struct BuildArgs {
|
||||||
|
@ -61,6 +61,7 @@ pub(crate) async fn build(args: BuildArgs) -> Result<PathBuf> {
|
||||||
let index = InMemoryIndex::default();
|
let index = InMemoryIndex::default();
|
||||||
let setup_py = SetupPyStrategy::default();
|
let setup_py = SetupPyStrategy::default();
|
||||||
let in_flight = InFlight::default();
|
let in_flight = InFlight::default();
|
||||||
|
let config_settings = ConfigSettings::default();
|
||||||
|
|
||||||
let build_dispatch = BuildDispatch::new(
|
let build_dispatch = BuildDispatch::new(
|
||||||
&client,
|
&client,
|
||||||
|
@ -72,6 +73,7 @@ pub(crate) async fn build(args: BuildArgs) -> Result<PathBuf> {
|
||||||
&in_flight,
|
&in_flight,
|
||||||
venv.python_executable(),
|
venv.python_executable(),
|
||||||
setup_py,
|
setup_py,
|
||||||
|
&config_settings,
|
||||||
&NoBuild::None,
|
&NoBuild::None,
|
||||||
&NoBinary::None,
|
&NoBinary::None,
|
||||||
);
|
);
|
||||||
|
@ -84,6 +86,7 @@ pub(crate) async fn build(args: BuildArgs) -> Result<PathBuf> {
|
||||||
SourceBuildContext::default(),
|
SourceBuildContext::default(),
|
||||||
args.sdist.display().to_string(),
|
args.sdist.display().to_string(),
|
||||||
setup_py,
|
setup_py,
|
||||||
|
config_settings.clone(),
|
||||||
build_kind,
|
build_kind,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -25,7 +25,7 @@ use uv_installer::{Downloader, NoBinary};
|
||||||
use uv_interpreter::Virtualenv;
|
use uv_interpreter::Virtualenv;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_resolver::{DistFinder, InMemoryIndex};
|
use uv_resolver::{DistFinder, InMemoryIndex};
|
||||||
use uv_traits::{BuildContext, InFlight, NoBuild, SetupPyStrategy};
|
use uv_traits::{BuildContext, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
pub(crate) struct InstallManyArgs {
|
pub(crate) struct InstallManyArgs {
|
||||||
|
@ -65,12 +65,12 @@ pub(crate) async fn install_many(args: InstallManyArgs) -> Result<()> {
|
||||||
let setup_py = SetupPyStrategy::default();
|
let setup_py = SetupPyStrategy::default();
|
||||||
let in_flight = InFlight::default();
|
let in_flight = InFlight::default();
|
||||||
let tags = venv.interpreter().tags()?;
|
let tags = venv.interpreter().tags()?;
|
||||||
|
|
||||||
let no_build = if args.no_build {
|
let no_build = if args.no_build {
|
||||||
NoBuild::All
|
NoBuild::All
|
||||||
} else {
|
} else {
|
||||||
NoBuild::None
|
NoBuild::None
|
||||||
};
|
};
|
||||||
|
let config_settings = ConfigSettings::default();
|
||||||
|
|
||||||
let build_dispatch = BuildDispatch::new(
|
let build_dispatch = BuildDispatch::new(
|
||||||
&client,
|
&client,
|
||||||
|
@ -82,6 +82,7 @@ pub(crate) async fn install_many(args: InstallManyArgs) -> Result<()> {
|
||||||
&in_flight,
|
&in_flight,
|
||||||
venv.python_executable(),
|
venv.python_executable(),
|
||||||
setup_py,
|
setup_py,
|
||||||
|
&config_settings,
|
||||||
&no_build,
|
&no_build,
|
||||||
&NoBinary::None,
|
&NoBinary::None,
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,7 +18,7 @@ use uv_dispatch::BuildDispatch;
|
||||||
use uv_installer::NoBinary;
|
use uv_installer::NoBinary;
|
||||||
use uv_interpreter::Virtualenv;
|
use uv_interpreter::Virtualenv;
|
||||||
use uv_resolver::{InMemoryIndex, Manifest, Options, Resolver};
|
use uv_resolver::{InMemoryIndex, Manifest, Options, Resolver};
|
||||||
use uv_traits::{InFlight, NoBuild, SetupPyStrategy};
|
use uv_traits::{ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
|
||||||
|
|
||||||
#[derive(ValueEnum, Default, Clone)]
|
#[derive(ValueEnum, Default, Clone)]
|
||||||
pub(crate) enum ResolveCliFormat {
|
pub(crate) enum ResolveCliFormat {
|
||||||
|
@ -72,12 +72,12 @@ pub(crate) async fn resolve_cli(args: ResolveCliArgs) -> Result<()> {
|
||||||
};
|
};
|
||||||
let index = InMemoryIndex::default();
|
let index = InMemoryIndex::default();
|
||||||
let in_flight = InFlight::default();
|
let in_flight = InFlight::default();
|
||||||
|
|
||||||
let no_build = if args.no_build {
|
let no_build = if args.no_build {
|
||||||
NoBuild::All
|
NoBuild::All
|
||||||
} else {
|
} else {
|
||||||
NoBuild::None
|
NoBuild::None
|
||||||
};
|
};
|
||||||
|
let config_settings = ConfigSettings::default();
|
||||||
|
|
||||||
let build_dispatch = BuildDispatch::new(
|
let build_dispatch = BuildDispatch::new(
|
||||||
&client,
|
&client,
|
||||||
|
@ -89,6 +89,7 @@ pub(crate) async fn resolve_cli(args: ResolveCliArgs) -> Result<()> {
|
||||||
&in_flight,
|
&in_flight,
|
||||||
venv.python_executable(),
|
venv.python_executable(),
|
||||||
SetupPyStrategy::default(),
|
SetupPyStrategy::default(),
|
||||||
|
&config_settings,
|
||||||
&no_build,
|
&no_build,
|
||||||
&NoBinary::None,
|
&NoBinary::None,
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,7 +21,7 @@ use uv_installer::NoBinary;
|
||||||
use uv_interpreter::Virtualenv;
|
use uv_interpreter::Virtualenv;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_resolver::InMemoryIndex;
|
use uv_resolver::InMemoryIndex;
|
||||||
use uv_traits::{BuildContext, InFlight, NoBuild, SetupPyStrategy};
|
use uv_traits::{BuildContext, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
pub(crate) struct ResolveManyArgs {
|
pub(crate) struct ResolveManyArgs {
|
||||||
|
@ -96,6 +96,7 @@ pub(crate) async fn resolve_many(args: ResolveManyArgs) -> Result<()> {
|
||||||
let index_locations = IndexLocations::default();
|
let index_locations = IndexLocations::default();
|
||||||
let setup_py = SetupPyStrategy::default();
|
let setup_py = SetupPyStrategy::default();
|
||||||
let flat_index = FlatIndex::default();
|
let flat_index = FlatIndex::default();
|
||||||
|
let config_settings = ConfigSettings::default();
|
||||||
|
|
||||||
// Create a `BuildDispatch` for each requirement.
|
// Create a `BuildDispatch` for each requirement.
|
||||||
let build_dispatch = BuildDispatch::new(
|
let build_dispatch = BuildDispatch::new(
|
||||||
|
@ -108,6 +109,7 @@ pub(crate) async fn resolve_many(args: ResolveManyArgs) -> Result<()> {
|
||||||
&in_flight,
|
&in_flight,
|
||||||
venv.python_executable(),
|
venv.python_executable(),
|
||||||
setup_py,
|
setup_py,
|
||||||
|
&config_settings,
|
||||||
&no_build,
|
&no_build,
|
||||||
&NoBinary::None,
|
&NoBinary::None,
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,7 +18,7 @@ use uv_client::{FlatIndex, RegistryClient};
|
||||||
use uv_installer::{Downloader, Installer, NoBinary, Plan, Planner, Reinstall, SitePackages};
|
use uv_installer::{Downloader, Installer, NoBinary, Plan, Planner, Reinstall, SitePackages};
|
||||||
use uv_interpreter::{Interpreter, Virtualenv};
|
use uv_interpreter::{Interpreter, Virtualenv};
|
||||||
use uv_resolver::{InMemoryIndex, Manifest, Options, Resolver};
|
use uv_resolver::{InMemoryIndex, Manifest, Options, Resolver};
|
||||||
use uv_traits::{BuildContext, BuildKind, InFlight, NoBuild, SetupPyStrategy};
|
use uv_traits::{BuildContext, BuildKind, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
|
||||||
|
|
||||||
/// The main implementation of [`BuildContext`], used by the CLI, see [`BuildContext`]
|
/// The main implementation of [`BuildContext`], used by the CLI, see [`BuildContext`]
|
||||||
/// documentation.
|
/// documentation.
|
||||||
|
@ -34,6 +34,7 @@ pub struct BuildDispatch<'a> {
|
||||||
setup_py: SetupPyStrategy,
|
setup_py: SetupPyStrategy,
|
||||||
no_build: &'a NoBuild,
|
no_build: &'a NoBuild,
|
||||||
no_binary: &'a NoBinary,
|
no_binary: &'a NoBinary,
|
||||||
|
config_settings: &'a ConfigSettings,
|
||||||
source_build_context: SourceBuildContext,
|
source_build_context: SourceBuildContext,
|
||||||
options: Options,
|
options: Options,
|
||||||
}
|
}
|
||||||
|
@ -50,6 +51,7 @@ impl<'a> BuildDispatch<'a> {
|
||||||
in_flight: &'a InFlight,
|
in_flight: &'a InFlight,
|
||||||
base_python: PathBuf,
|
base_python: PathBuf,
|
||||||
setup_py: SetupPyStrategy,
|
setup_py: SetupPyStrategy,
|
||||||
|
config_settings: &'a ConfigSettings,
|
||||||
no_build: &'a NoBuild,
|
no_build: &'a NoBuild,
|
||||||
no_binary: &'a NoBinary,
|
no_binary: &'a NoBinary,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -63,6 +65,7 @@ impl<'a> BuildDispatch<'a> {
|
||||||
in_flight,
|
in_flight,
|
||||||
base_python,
|
base_python,
|
||||||
setup_py,
|
setup_py,
|
||||||
|
config_settings,
|
||||||
no_build,
|
no_build,
|
||||||
no_binary,
|
no_binary,
|
||||||
source_build_context: SourceBuildContext::default(),
|
source_build_context: SourceBuildContext::default(),
|
||||||
|
@ -279,6 +282,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
|
||||||
self.source_build_context.clone(),
|
self.source_build_context.clone(),
|
||||||
package_id.to_string(),
|
package_id.to_string(),
|
||||||
self.setup_py,
|
self.setup_py,
|
||||||
|
self.config_settings.clone(),
|
||||||
build_kind,
|
build_kind,
|
||||||
)
|
)
|
||||||
.boxed()
|
.boxed()
|
||||||
|
|
|
@ -13,6 +13,7 @@ license = { workspace = true }
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
clap = { workspace = true, optional = true }
|
||||||
distribution-types = { path = "../distribution-types" }
|
distribution-types = { path = "../distribution-types" }
|
||||||
once-map = { path = "../once-map" }
|
once-map = { path = "../once-map" }
|
||||||
pep508_rs = { path = "../pep508-rs" }
|
pep508_rs = { path = "../pep508-rs" }
|
||||||
|
@ -21,4 +22,10 @@ uv-interpreter = { path = "../uv-interpreter" }
|
||||||
uv-normalize = { path = "../uv-normalize" }
|
uv-normalize = { path = "../uv-normalize" }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
serde = { workspace = true, optional = true }
|
||||||
|
serde_json = { workspace = true, optional = true }
|
||||||
tokio = { workspace = true, features = ["sync"] }
|
tokio = { workspace = true, features = ["sync"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
serde = ["dep:serde", "dep:serde_json"]
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
//! Avoid cyclic crate dependencies between resolver, installer and builder.
|
//! Avoid cyclic crate dependencies between resolver, installer and builder.
|
||||||
|
|
||||||
|
use std::collections::btree_map::Entry;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use serde::ser::SerializeMap;
|
||||||
|
|
||||||
use distribution_types::{CachedDist, DistributionId, IndexLocations, Resolution, SourceDist};
|
use distribution_types::{CachedDist, DistributionId, IndexLocations, Resolution, SourceDist};
|
||||||
use once_map::OnceMap;
|
use once_map::OnceMap;
|
||||||
|
@ -288,6 +291,94 @@ impl NoBuild {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ConfigSettingEntry {
|
||||||
|
/// The key of the setting. For example, given `key=value`, this would be `key`.
|
||||||
|
key: String,
|
||||||
|
/// The value of the setting. For example, given `key=value`, this would be `value`.
|
||||||
|
value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ConfigSettingEntry {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let Some((key, value)) = s.split_once('=') else {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Invalid config setting: {s} (expected `KEY=VALUE`)"
|
||||||
|
));
|
||||||
|
};
|
||||||
|
Ok(Self {
|
||||||
|
key: key.trim().to_string(),
|
||||||
|
value: value.trim().to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
enum ConfigSettingValue {
|
||||||
|
/// The value consists of a single string.
|
||||||
|
String(String),
|
||||||
|
/// The value consists of a list of strings.
|
||||||
|
List(Vec<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Settings to pass to a PEP 517 build backend, structured as a map from (string) key to string or
|
||||||
|
/// list of strings.
|
||||||
|
///
|
||||||
|
/// See: <https://peps.python.org/pep-0517/#config-settings>
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct ConfigSettings(BTreeMap<String, ConfigSettingValue>);
|
||||||
|
|
||||||
|
impl FromIterator<ConfigSettingEntry> for ConfigSettings {
|
||||||
|
fn from_iter<T: IntoIterator<Item = ConfigSettingEntry>>(iter: T) -> Self {
|
||||||
|
let mut config = BTreeMap::default();
|
||||||
|
for entry in iter {
|
||||||
|
match config.entry(entry.key) {
|
||||||
|
Entry::Vacant(vacant) => {
|
||||||
|
vacant.insert(ConfigSettingValue::String(entry.value));
|
||||||
|
}
|
||||||
|
Entry::Occupied(mut occupied) => match occupied.get_mut() {
|
||||||
|
ConfigSettingValue::String(existing) => {
|
||||||
|
let existing = existing.clone();
|
||||||
|
occupied.insert(ConfigSettingValue::List(vec![existing, entry.value]));
|
||||||
|
}
|
||||||
|
ConfigSettingValue::List(existing) => {
|
||||||
|
existing.push(entry.value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl ConfigSettings {
|
||||||
|
/// Convert the settings to a string that can be passed directly to a PEP 517 build backend.
|
||||||
|
pub fn escape_for_python(&self) -> String {
|
||||||
|
serde_json::to_string(self).expect("Failed to serialize config settings")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl serde::Serialize for ConfigSettings {
|
||||||
|
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
let mut map = serializer.serialize_map(Some(self.0.len()))?;
|
||||||
|
for (key, value) in &self.0 {
|
||||||
|
match value {
|
||||||
|
ConfigSettingValue::String(value) => {
|
||||||
|
map.serialize_entry(&key, &value)?;
|
||||||
|
}
|
||||||
|
ConfigSettingValue::List(values) => {
|
||||||
|
map.serialize_entry(&key, &values)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
|
@ -349,4 +440,81 @@ mod tests {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn collect_config_settings() {
|
||||||
|
let settings: ConfigSettings = vec![
|
||||||
|
ConfigSettingEntry {
|
||||||
|
key: "key".to_string(),
|
||||||
|
value: "value".to_string(),
|
||||||
|
},
|
||||||
|
ConfigSettingEntry {
|
||||||
|
key: "key".to_string(),
|
||||||
|
value: "value2".to_string(),
|
||||||
|
},
|
||||||
|
ConfigSettingEntry {
|
||||||
|
key: "list".to_string(),
|
||||||
|
value: "value3".to_string(),
|
||||||
|
},
|
||||||
|
ConfigSettingEntry {
|
||||||
|
key: "list".to_string(),
|
||||||
|
value: "value4".to_string(),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
assert_eq!(
|
||||||
|
settings.0.get("key"),
|
||||||
|
Some(&ConfigSettingValue::List(vec![
|
||||||
|
"value".to_string(),
|
||||||
|
"value2".to_string()
|
||||||
|
]))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
settings.0.get("list"),
|
||||||
|
Some(&ConfigSettingValue::List(vec![
|
||||||
|
"value3".to_string(),
|
||||||
|
"value4".to_string()
|
||||||
|
]))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
fn escape_for_python() {
|
||||||
|
let mut settings = ConfigSettings::default();
|
||||||
|
settings.0.insert(
|
||||||
|
"key".to_string(),
|
||||||
|
ConfigSettingValue::String("value".to_string()),
|
||||||
|
);
|
||||||
|
settings.0.insert(
|
||||||
|
"list".to_string(),
|
||||||
|
ConfigSettingValue::List(vec!["value1".to_string(), "value2".to_string()]),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
settings.escape_for_python(),
|
||||||
|
r#"{"key":"value","list":["value1","value2"]}"#
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut settings = ConfigSettings::default();
|
||||||
|
settings.0.insert(
|
||||||
|
"key".to_string(),
|
||||||
|
ConfigSettingValue::String("Hello, \"world!\"".to_string()),
|
||||||
|
);
|
||||||
|
settings.0.insert(
|
||||||
|
"list".to_string(),
|
||||||
|
ConfigSettingValue::List(vec!["'value1'".to_string()]),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
settings.escape_for_python(),
|
||||||
|
r#"{"key":"Hello, \"world!\"","list":["'value1'"]}"#
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut settings = ConfigSettings::default();
|
||||||
|
settings.0.insert(
|
||||||
|
"key".to_string(),
|
||||||
|
ConfigSettingValue::String("val\\1 {}ue".to_string()),
|
||||||
|
);
|
||||||
|
assert_eq!(settings.escape_for_python(), r#"{"key":"val\\1 {}ue"}"#);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ use uv_resolver::{
|
||||||
AnnotationStyle, DependencyMode, DisplayResolutionGraph, InMemoryIndex, Manifest,
|
AnnotationStyle, DependencyMode, DisplayResolutionGraph, InMemoryIndex, Manifest,
|
||||||
OptionsBuilder, PreReleaseMode, ResolutionMode, Resolver,
|
OptionsBuilder, PreReleaseMode, ResolutionMode, Resolver,
|
||||||
};
|
};
|
||||||
use uv_traits::{InFlight, NoBuild, SetupPyStrategy};
|
use uv_traits::{ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
|
||||||
use uv_warnings::warn_user;
|
use uv_warnings::warn_user;
|
||||||
|
|
||||||
use crate::commands::reporters::{DownloadReporter, ResolverReporter};
|
use crate::commands::reporters::{DownloadReporter, ResolverReporter};
|
||||||
|
@ -58,6 +58,7 @@ pub(crate) async fn pip_compile(
|
||||||
include_find_links: bool,
|
include_find_links: bool,
|
||||||
index_locations: IndexLocations,
|
index_locations: IndexLocations,
|
||||||
setup_py: SetupPyStrategy,
|
setup_py: SetupPyStrategy,
|
||||||
|
config_settings: ConfigSettings,
|
||||||
connectivity: Connectivity,
|
connectivity: Connectivity,
|
||||||
no_build: &NoBuild,
|
no_build: &NoBuild,
|
||||||
python_version: Option<PythonVersion>,
|
python_version: Option<PythonVersion>,
|
||||||
|
@ -219,6 +220,7 @@ pub(crate) async fn pip_compile(
|
||||||
&in_flight,
|
&in_flight,
|
||||||
interpreter.sys_executable().to_path_buf(),
|
interpreter.sys_executable().to_path_buf(),
|
||||||
setup_py,
|
setup_py,
|
||||||
|
&config_settings,
|
||||||
no_build,
|
no_build,
|
||||||
&NoBinary::None,
|
&NoBinary::None,
|
||||||
)
|
)
|
||||||
|
|
|
@ -33,7 +33,7 @@ use uv_resolver::{
|
||||||
DependencyMode, InMemoryIndex, Manifest, Options, OptionsBuilder, PreReleaseMode,
|
DependencyMode, InMemoryIndex, Manifest, Options, OptionsBuilder, PreReleaseMode,
|
||||||
ResolutionGraph, ResolutionMode, Resolver,
|
ResolutionGraph, ResolutionMode, Resolver,
|
||||||
};
|
};
|
||||||
use uv_traits::{InFlight, NoBuild, SetupPyStrategy};
|
use uv_traits::{ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
|
||||||
|
|
||||||
use crate::commands::reporters::{DownloadReporter, InstallReporter, ResolverReporter};
|
use crate::commands::reporters::{DownloadReporter, InstallReporter, ResolverReporter};
|
||||||
use crate::commands::{elapsed, ChangeEvent, ChangeEventKind, ExitStatus};
|
use crate::commands::{elapsed, ChangeEvent, ChangeEventKind, ExitStatus};
|
||||||
|
@ -58,6 +58,7 @@ pub(crate) async fn pip_install(
|
||||||
link_mode: LinkMode,
|
link_mode: LinkMode,
|
||||||
setup_py: SetupPyStrategy,
|
setup_py: SetupPyStrategy,
|
||||||
connectivity: Connectivity,
|
connectivity: Connectivity,
|
||||||
|
config_settings: &ConfigSettings,
|
||||||
no_build: &NoBuild,
|
no_build: &NoBuild,
|
||||||
no_binary: &NoBinary,
|
no_binary: &NoBinary,
|
||||||
strict: bool,
|
strict: bool,
|
||||||
|
@ -173,6 +174,7 @@ pub(crate) async fn pip_install(
|
||||||
&in_flight,
|
&in_flight,
|
||||||
venv.python_executable(),
|
venv.python_executable(),
|
||||||
setup_py,
|
setup_py,
|
||||||
|
config_settings,
|
||||||
no_build,
|
no_build,
|
||||||
no_binary,
|
no_binary,
|
||||||
)
|
)
|
||||||
|
@ -255,6 +257,7 @@ pub(crate) async fn pip_install(
|
||||||
&in_flight,
|
&in_flight,
|
||||||
venv.python_executable(),
|
venv.python_executable(),
|
||||||
setup_py,
|
setup_py,
|
||||||
|
config_settings,
|
||||||
no_build,
|
no_build,
|
||||||
no_binary,
|
no_binary,
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,7 +20,7 @@ use uv_installer::{
|
||||||
};
|
};
|
||||||
use uv_interpreter::Virtualenv;
|
use uv_interpreter::Virtualenv;
|
||||||
use uv_resolver::InMemoryIndex;
|
use uv_resolver::InMemoryIndex;
|
||||||
use uv_traits::{InFlight, NoBuild, SetupPyStrategy};
|
use uv_traits::{ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
|
||||||
|
|
||||||
use crate::commands::reporters::{DownloadReporter, FinderReporter, InstallReporter};
|
use crate::commands::reporters::{DownloadReporter, FinderReporter, InstallReporter};
|
||||||
use crate::commands::{elapsed, ChangeEvent, ChangeEventKind, ExitStatus};
|
use crate::commands::{elapsed, ChangeEvent, ChangeEventKind, ExitStatus};
|
||||||
|
@ -36,6 +36,7 @@ pub(crate) async fn pip_sync(
|
||||||
index_locations: IndexLocations,
|
index_locations: IndexLocations,
|
||||||
setup_py: SetupPyStrategy,
|
setup_py: SetupPyStrategy,
|
||||||
connectivity: Connectivity,
|
connectivity: Connectivity,
|
||||||
|
config_settings: &ConfigSettings,
|
||||||
no_build: &NoBuild,
|
no_build: &NoBuild,
|
||||||
no_binary: &NoBinary,
|
no_binary: &NoBinary,
|
||||||
strict: bool,
|
strict: bool,
|
||||||
|
@ -112,6 +113,7 @@ pub(crate) async fn pip_sync(
|
||||||
&in_flight,
|
&in_flight,
|
||||||
venv.python_executable(),
|
venv.python_executable(),
|
||||||
setup_py,
|
setup_py,
|
||||||
|
config_settings,
|
||||||
no_build,
|
no_build,
|
||||||
no_binary,
|
no_binary,
|
||||||
);
|
);
|
||||||
|
|
|
@ -22,7 +22,7 @@ use uv_fs::Normalized;
|
||||||
use uv_installer::NoBinary;
|
use uv_installer::NoBinary;
|
||||||
use uv_interpreter::{find_default_python, find_requested_python, Error};
|
use uv_interpreter::{find_default_python, find_requested_python, Error};
|
||||||
use uv_resolver::{InMemoryIndex, OptionsBuilder};
|
use uv_resolver::{InMemoryIndex, OptionsBuilder};
|
||||||
use uv_traits::{BuildContext, InFlight, NoBuild, SetupPyStrategy};
|
use uv_traits::{BuildContext, ConfigSettings, InFlight, NoBuild, SetupPyStrategy};
|
||||||
|
|
||||||
use crate::commands::ExitStatus;
|
use crate::commands::ExitStatus;
|
||||||
use crate::printer::Printer;
|
use crate::printer::Printer;
|
||||||
|
@ -154,6 +154,9 @@ async fn venv_impl(
|
||||||
// Track in-flight downloads, builds, etc., across resolutions.
|
// Track in-flight downloads, builds, etc., across resolutions.
|
||||||
let in_flight = InFlight::default();
|
let in_flight = InFlight::default();
|
||||||
|
|
||||||
|
// For seed packages, assume the default settings are sufficient.
|
||||||
|
let config_settings = ConfigSettings::default();
|
||||||
|
|
||||||
// Prep the build context.
|
// Prep the build context.
|
||||||
let build_dispatch = BuildDispatch::new(
|
let build_dispatch = BuildDispatch::new(
|
||||||
&client,
|
&client,
|
||||||
|
@ -165,6 +168,7 @@ async fn venv_impl(
|
||||||
&in_flight,
|
&in_flight,
|
||||||
venv.python_executable(),
|
venv.python_executable(),
|
||||||
SetupPyStrategy::default(),
|
SetupPyStrategy::default(),
|
||||||
|
&config_settings,
|
||||||
&NoBuild::All,
|
&NoBuild::All,
|
||||||
&NoBinary::None,
|
&NoBinary::None,
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,7 +20,9 @@ use uv_installer::{NoBinary, Reinstall};
|
||||||
use uv_interpreter::PythonVersion;
|
use uv_interpreter::PythonVersion;
|
||||||
use uv_normalize::{ExtraName, PackageName};
|
use uv_normalize::{ExtraName, PackageName};
|
||||||
use uv_resolver::{AnnotationStyle, DependencyMode, PreReleaseMode, ResolutionMode};
|
use uv_resolver::{AnnotationStyle, DependencyMode, PreReleaseMode, ResolutionMode};
|
||||||
use uv_traits::{NoBuild, PackageNameSpecifier, SetupPyStrategy};
|
use uv_traits::{
|
||||||
|
ConfigSettingEntry, ConfigSettings, NoBuild, PackageNameSpecifier, SetupPyStrategy,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::commands::{extra_name_with_clap_error, ExitStatus, Upgrade};
|
use crate::commands::{extra_name_with_clap_error, ExitStatus, Upgrade};
|
||||||
use crate::compat::CompatArgs;
|
use crate::compat::CompatArgs;
|
||||||
|
@ -331,6 +333,10 @@ struct PipCompileArgs {
|
||||||
#[clap(long, conflicts_with = "no_build")]
|
#[clap(long, conflicts_with = "no_build")]
|
||||||
only_binary: Vec<PackageNameSpecifier>,
|
only_binary: Vec<PackageNameSpecifier>,
|
||||||
|
|
||||||
|
/// Settings to pass to the PEP 517 build backend, specified as `KEY=VALUE` pairs.
|
||||||
|
#[clap(long, short = 'C', alias = "config-settings")]
|
||||||
|
config_setting: Vec<ConfigSettingEntry>,
|
||||||
|
|
||||||
/// The minimum Python version that should be supported by the compiled requirements (e.g.,
|
/// The minimum Python version that should be supported by the compiled requirements (e.g.,
|
||||||
/// `3.7` or `3.7.9`).
|
/// `3.7` or `3.7.9`).
|
||||||
///
|
///
|
||||||
|
@ -456,6 +462,10 @@ struct PipSyncArgs {
|
||||||
#[clap(long, conflicts_with = "no_build")]
|
#[clap(long, conflicts_with = "no_build")]
|
||||||
only_binary: Vec<PackageNameSpecifier>,
|
only_binary: Vec<PackageNameSpecifier>,
|
||||||
|
|
||||||
|
/// Settings to pass to the PEP 517 build backend, specified as `KEY=VALUE` pairs.
|
||||||
|
#[clap(long, short = 'C', alias = "config-settings")]
|
||||||
|
config_setting: Vec<ConfigSettingEntry>,
|
||||||
|
|
||||||
/// Validate the virtual environment after completing the installation, to detect packages with
|
/// Validate the virtual environment after completing the installation, to detect packages with
|
||||||
/// missing dependencies or other issues.
|
/// missing dependencies or other issues.
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
|
@ -621,6 +631,10 @@ struct PipInstallArgs {
|
||||||
#[clap(long, conflicts_with = "no_build")]
|
#[clap(long, conflicts_with = "no_build")]
|
||||||
only_binary: Vec<PackageNameSpecifier>,
|
only_binary: Vec<PackageNameSpecifier>,
|
||||||
|
|
||||||
|
/// Settings to pass to the PEP 517 build backend, specified as `KEY=VALUE` pairs.
|
||||||
|
#[clap(long, short = 'C', alias = "config-settings")]
|
||||||
|
config_setting: Vec<ConfigSettingEntry>,
|
||||||
|
|
||||||
/// Validate the virtual environment after completing the installation, to detect packages with
|
/// Validate the virtual environment after completing the installation, to detect packages with
|
||||||
/// missing dependencies or other issues.
|
/// missing dependencies or other issues.
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
|
@ -873,6 +887,12 @@ async fn run() -> Result<ExitStatus> {
|
||||||
} else {
|
} else {
|
||||||
DependencyMode::Transitive
|
DependencyMode::Transitive
|
||||||
};
|
};
|
||||||
|
let setup_py = if args.legacy_setup_py {
|
||||||
|
SetupPyStrategy::Setuptools
|
||||||
|
} else {
|
||||||
|
SetupPyStrategy::Pep517
|
||||||
|
};
|
||||||
|
let config_settings = args.config_setting.into_iter().collect::<ConfigSettings>();
|
||||||
commands::pip_compile(
|
commands::pip_compile(
|
||||||
&requirements,
|
&requirements,
|
||||||
&constraints,
|
&constraints,
|
||||||
|
@ -889,11 +909,8 @@ async fn run() -> Result<ExitStatus> {
|
||||||
args.emit_index_url,
|
args.emit_index_url,
|
||||||
args.emit_find_links,
|
args.emit_find_links,
|
||||||
index_urls,
|
index_urls,
|
||||||
if args.legacy_setup_py {
|
setup_py,
|
||||||
SetupPyStrategy::Setuptools
|
config_settings,
|
||||||
} else {
|
|
||||||
SetupPyStrategy::Pep517
|
|
||||||
},
|
|
||||||
if args.offline {
|
if args.offline {
|
||||||
Connectivity::Offline
|
Connectivity::Offline
|
||||||
} else {
|
} else {
|
||||||
|
@ -928,21 +945,25 @@ async fn run() -> Result<ExitStatus> {
|
||||||
let reinstall = Reinstall::from_args(args.reinstall, args.reinstall_package);
|
let reinstall = Reinstall::from_args(args.reinstall, args.reinstall_package);
|
||||||
let no_binary = NoBinary::from_args(args.no_binary);
|
let no_binary = NoBinary::from_args(args.no_binary);
|
||||||
let no_build = NoBuild::from_args(args.only_binary, args.no_build);
|
let no_build = NoBuild::from_args(args.only_binary, args.no_build);
|
||||||
|
let setup_py = if args.legacy_setup_py {
|
||||||
|
SetupPyStrategy::Setuptools
|
||||||
|
} else {
|
||||||
|
SetupPyStrategy::Pep517
|
||||||
|
};
|
||||||
|
let config_settings = args.config_setting.into_iter().collect::<ConfigSettings>();
|
||||||
|
|
||||||
commands::pip_sync(
|
commands::pip_sync(
|
||||||
&sources,
|
&sources,
|
||||||
&reinstall,
|
&reinstall,
|
||||||
args.link_mode,
|
args.link_mode,
|
||||||
index_urls,
|
index_urls,
|
||||||
if args.legacy_setup_py {
|
setup_py,
|
||||||
SetupPyStrategy::Setuptools
|
|
||||||
} else {
|
|
||||||
SetupPyStrategy::Pep517
|
|
||||||
},
|
|
||||||
if args.offline {
|
if args.offline {
|
||||||
Connectivity::Offline
|
Connectivity::Offline
|
||||||
} else {
|
} else {
|
||||||
Connectivity::Online
|
Connectivity::Online
|
||||||
},
|
},
|
||||||
|
&config_settings,
|
||||||
&no_build,
|
&no_build,
|
||||||
&no_binary,
|
&no_binary,
|
||||||
args.strict,
|
args.strict,
|
||||||
|
@ -998,6 +1019,13 @@ async fn run() -> Result<ExitStatus> {
|
||||||
} else {
|
} else {
|
||||||
DependencyMode::Transitive
|
DependencyMode::Transitive
|
||||||
};
|
};
|
||||||
|
let setup_py = if args.legacy_setup_py {
|
||||||
|
SetupPyStrategy::Setuptools
|
||||||
|
} else {
|
||||||
|
SetupPyStrategy::Pep517
|
||||||
|
};
|
||||||
|
let config_settings = args.config_setting.into_iter().collect::<ConfigSettings>();
|
||||||
|
|
||||||
commands::pip_install(
|
commands::pip_install(
|
||||||
&requirements,
|
&requirements,
|
||||||
&constraints,
|
&constraints,
|
||||||
|
@ -1010,16 +1038,13 @@ async fn run() -> Result<ExitStatus> {
|
||||||
index_urls,
|
index_urls,
|
||||||
&reinstall,
|
&reinstall,
|
||||||
args.link_mode,
|
args.link_mode,
|
||||||
if args.legacy_setup_py {
|
setup_py,
|
||||||
SetupPyStrategy::Setuptools
|
|
||||||
} else {
|
|
||||||
SetupPyStrategy::Pep517
|
|
||||||
},
|
|
||||||
if args.offline {
|
if args.offline {
|
||||||
Connectivity::Offline
|
Connectivity::Offline
|
||||||
} else {
|
} else {
|
||||||
Connectivity::Online
|
Connectivity::Online
|
||||||
},
|
},
|
||||||
|
&config_settings,
|
||||||
&no_build,
|
&no_build,
|
||||||
&no_binary,
|
&no_binary,
|
||||||
args.strict,
|
args.strict,
|
||||||
|
|
|
@ -1598,3 +1598,105 @@ fn launcher_with_symlink() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn config_settings() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let current_dir = std::env::current_dir()?;
|
||||||
|
let workspace_dir = regex::escape(
|
||||||
|
Url::from_directory_path(current_dir.join("..").join("..").canonicalize()?)
|
||||||
|
.unwrap()
|
||||||
|
.as_str(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let filters = [(workspace_dir.as_str(), "file://[WORKSPACE_DIR]/")]
|
||||||
|
.into_iter()
|
||||||
|
.chain(INSTA_FILTERS.to_vec())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Install the editable package.
|
||||||
|
uv_snapshot!(filters, Command::new(get_bin())
|
||||||
|
.arg("pip")
|
||||||
|
.arg("install")
|
||||||
|
.arg("-e")
|
||||||
|
.arg("../../scripts/editable-installs/setuptools_editable")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(context.cache_dir.path())
|
||||||
|
.arg("--exclude-newer")
|
||||||
|
.arg(EXCLUDE_NEWER)
|
||||||
|
.env("VIRTUAL_ENV", context.venv.as_os_str())
|
||||||
|
.env("CARGO_TARGET_DIR", "../../../target/target_install_editable"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Built 1 editable in [TIME]
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
Downloaded 1 package in [TIME]
|
||||||
|
Installed 2 packages in [TIME]
|
||||||
|
+ iniconfig==2.0.0
|
||||||
|
+ setuptools-editable==0.1.0 (from file://[WORKSPACE_DIR]/scripts/editable-installs/setuptools_editable)
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
// When installed without `--editable_mode=compat`, the `finder.py` file should be present.
|
||||||
|
let finder = context
|
||||||
|
.venv
|
||||||
|
.join("lib/python3.12/site-packages")
|
||||||
|
.join("__editable___setuptools_editable_0_1_0_finder.py");
|
||||||
|
assert!(finder.exists());
|
||||||
|
|
||||||
|
// Install the editable package with `--editable_mode=compat`.
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let current_dir = std::env::current_dir()?;
|
||||||
|
let workspace_dir = regex::escape(
|
||||||
|
Url::from_directory_path(current_dir.join("..").join("..").canonicalize()?)
|
||||||
|
.unwrap()
|
||||||
|
.as_str(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let filters = [(workspace_dir.as_str(), "file://[WORKSPACE_DIR]/")]
|
||||||
|
.into_iter()
|
||||||
|
.chain(INSTA_FILTERS.to_vec())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
uv_snapshot!(filters, Command::new(get_bin())
|
||||||
|
.arg("pip")
|
||||||
|
.arg("install")
|
||||||
|
.arg("-e")
|
||||||
|
.arg("../../scripts/editable-installs/setuptools_editable")
|
||||||
|
.arg("-C")
|
||||||
|
.arg("editable_mode=compat")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(context.cache_dir.path())
|
||||||
|
.arg("--exclude-newer")
|
||||||
|
.arg(EXCLUDE_NEWER)
|
||||||
|
.env("VIRTUAL_ENV", context.venv.as_os_str())
|
||||||
|
.env("CARGO_TARGET_DIR", "../../../target/target_install_editable"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Built 1 editable in [TIME]
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
Downloaded 1 package in [TIME]
|
||||||
|
Installed 2 packages in [TIME]
|
||||||
|
+ iniconfig==2.0.0
|
||||||
|
+ setuptools-editable==0.1.0 (from file://[WORKSPACE_DIR]/scripts/editable-installs/setuptools_editable)
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
// When installed without `--editable_mode=compat`, the `finder.py` file should _not_ be present.
|
||||||
|
let finder = context
|
||||||
|
.venv
|
||||||
|
.join("lib/python3.12/site-packages")
|
||||||
|
.join("__editable___setuptools_editable_0_1_0_finder.py");
|
||||||
|
assert!(!finder.exists());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
2
scripts/editable-installs/setuptools_editable/.gitignore
vendored
Normal file
2
scripts/editable-installs/setuptools_editable/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Artifacts from the build process.
|
||||||
|
*.egg-info/
|
13
scripts/editable-installs/setuptools_editable/pyproject.toml
Normal file
13
scripts/editable-installs/setuptools_editable/pyproject.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[project]
|
||||||
|
name = "setuptools_editable"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Default template for a setuptools project"
|
||||||
|
authors = [
|
||||||
|
{name = "konstin", email = "konstin@mailbox.org"},
|
||||||
|
]
|
||||||
|
dependencies = ["iniconfig"]
|
||||||
|
requires-python = ">=3.11,<3.13"
|
||||||
|
license = {text = "MIT"}
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
anyio = ["anyio>=3.3.0"]
|
|
@ -0,0 +1,2 @@
|
||||||
|
def a():
|
||||||
|
pass
|
Loading…
Add table
Add a link
Reference in a new issue