mirror of https://github.com/astral-sh/uv
Add script sources support
This commit is contained in:
parent
1d2d9aa193
commit
db10dcbf24
|
|
@ -22,6 +22,7 @@ use uv_configuration::{
|
|||
};
|
||||
use uv_configuration::{BuildOutput, Concurrency};
|
||||
use uv_distribution::DistributionDatabase;
|
||||
use uv_distribution::ExtraBuildRequires;
|
||||
use uv_distribution_filename::DistFilename;
|
||||
use uv_distribution_types::{
|
||||
CachedDist, DependencyMetadata, Identifier, IndexCapabilities, IndexLocations,
|
||||
|
|
@ -40,7 +41,6 @@ use uv_types::{
|
|||
HashStrategy, InFlight,
|
||||
};
|
||||
use uv_workspace::WorkspaceCache;
|
||||
use uv_distribution::ExtraBuildRequires;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum BuildDispatchError {
|
||||
|
|
|
|||
|
|
@ -261,13 +261,15 @@ impl ExtraBuildRequires {
|
|||
workspace,
|
||||
None,
|
||||
)
|
||||
.map(move |requirement| match requirement {
|
||||
Ok(requirement) => Ok(requirement.into_inner().into()),
|
||||
Err(err) => Err(MetadataError::LoweringError(
|
||||
requirement_name.clone(),
|
||||
Box::new(err),
|
||||
)),
|
||||
})
|
||||
.map(
|
||||
move |requirement| match requirement {
|
||||
Ok(requirement) => Ok(requirement.into_inner().into()),
|
||||
Err(err) => Err(MetadataError::LoweringError(
|
||||
requirement_name.clone(),
|
||||
Box::new(err),
|
||||
)),
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
result.insert(package_name, lowered);
|
||||
|
|
|
|||
|
|
@ -381,6 +381,8 @@ pub struct ToolUv {
|
|||
pub override_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
|
||||
pub constraint_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
|
||||
pub build_constraint_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
|
||||
pub extra_build_dependencies:
|
||||
Option<BTreeMap<PackageName, Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>>,
|
||||
pub sources: Option<BTreeMap<PackageName, Sources>>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -564,9 +564,8 @@ async fn build_package(
|
|||
let workspace_cache = WorkspaceCache::default();
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let extra_build_requires =
|
||||
uv_distribution::ExtraBuildRequires::from_lowered(extra_build_dependencies.clone());
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
cache,
|
||||
|
|
|
|||
|
|
@ -477,9 +477,8 @@ pub(crate) async fn pip_compile(
|
|||
.map(|constraint| constraint.requirement.clone()),
|
||||
);
|
||||
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let extra_build_requires =
|
||||
uv_distribution::ExtraBuildRequires::from_lowered(extra_build_dependencies.clone());
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
&cache,
|
||||
|
|
|
|||
|
|
@ -420,9 +420,8 @@ pub(crate) async fn pip_install(
|
|||
let state = SharedState::default();
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let extra_build_requires =
|
||||
uv_distribution::ExtraBuildRequires::from_lowered(extra_build_dependencies.clone());
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
&cache,
|
||||
|
|
|
|||
|
|
@ -355,9 +355,8 @@ pub(crate) async fn pip_sync(
|
|||
let state = SharedState::default();
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let extra_build_requires =
|
||||
uv_distribution::ExtraBuildRequires::from_lowered(extra_build_dependencies.clone());
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
&cache,
|
||||
|
|
|
|||
|
|
@ -1298,6 +1298,7 @@ impl PythonTarget {
|
|||
|
||||
/// Represents the destination where dependencies are added, either to a project or a script.
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub(super) enum AddTarget {
|
||||
/// A PEP 723 script, with inline metadata.
|
||||
Script(Pep723Script, Box<Interpreter>),
|
||||
|
|
@ -1398,6 +1399,7 @@ impl AddTarget {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum AddTargetSnapshot {
|
||||
Script(Pep723Script, Option<Vec<u8>>),
|
||||
Project(VirtualProject, Option<Vec<u8>>),
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ use crate::printer::Printer;
|
|||
use crate::settings::{NetworkSettings, ResolverSettings};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum ExportTarget {
|
||||
/// A PEP 723 script, with inline metadata.
|
||||
Script(Pep723Script),
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ use crate::commands::pip::loggers::{DefaultResolveLogger, ResolveLogger, Summary
|
|||
use crate::commands::project::lock_target::LockTarget;
|
||||
use crate::commands::project::{
|
||||
ProjectError, ProjectInterpreter, ScriptInterpreter, UniversalState,
|
||||
init_script_python_requirement,
|
||||
init_script_python_requirement, script_specification,
|
||||
};
|
||||
use crate::commands::reporters::{PythonDownloadReporter, ResolverReporter};
|
||||
use crate::commands::{ExitStatus, ScriptPath, diagnostics, pip};
|
||||
|
|
@ -678,9 +678,16 @@ async fn do_lock(
|
|||
index_locations,
|
||||
*sources,
|
||||
)?,
|
||||
LockTarget::Script(_) => uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
),
|
||||
LockTarget::Script(script) => {
|
||||
// Try to get extra build dependencies from the script metadata
|
||||
script_specification(Pep723ItemRef::Script(script), settings)?
|
||||
.map(|spec| spec.extra_build_requires)
|
||||
.unwrap_or_else(|| {
|
||||
uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
)
|
||||
})
|
||||
}
|
||||
};
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy};
|
|||
use uv_virtualenv::remove_virtualenv;
|
||||
use uv_warnings::{warn_user, warn_user_once};
|
||||
use uv_workspace::dependency_groups::DependencyGroupError;
|
||||
use uv_workspace::pyproject::ExtraBuildDependencies;
|
||||
use uv_workspace::pyproject::PyProjectToml;
|
||||
use uv_workspace::{RequiresPythonSources, Workspace, WorkspaceCache};
|
||||
|
||||
|
|
@ -1741,9 +1742,8 @@ pub(crate) async fn resolve_names(
|
|||
let build_hasher = HashStrategy::default();
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let extra_build_requires =
|
||||
uv_distribution::ExtraBuildRequires::from_lowered(extra_build_dependencies.clone());
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
cache,
|
||||
|
|
@ -1954,9 +1954,8 @@ pub(crate) async fn resolve_environment(
|
|||
let workspace_cache = WorkspaceCache::default();
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let extra_build_requires =
|
||||
uv_distribution::ExtraBuildRequires::from_lowered(extra_build_dependencies.clone());
|
||||
let resolve_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
cache,
|
||||
|
|
@ -2097,9 +2096,8 @@ pub(crate) async fn sync_environment(
|
|||
};
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let extra_build_requires =
|
||||
uv_distribution::ExtraBuildRequires::from_lowered(extra_build_dependencies.clone());
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
cache,
|
||||
|
|
@ -2157,6 +2155,15 @@ pub(crate) async fn sync_environment(
|
|||
Ok(venv)
|
||||
}
|
||||
|
||||
/// A script specification that includes both requirements and extra build dependencies.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ScriptSpecification {
|
||||
/// The requirements specification for the script.
|
||||
pub(crate) requirements: RequirementsSpecification,
|
||||
/// The extra build dependencies for the script.
|
||||
pub(crate) extra_build_requires: uv_distribution::ExtraBuildRequires,
|
||||
}
|
||||
|
||||
/// The result of updating a [`PythonEnvironment`] to satisfy a set of [`RequirementsSource`]s.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct EnvironmentUpdate {
|
||||
|
|
@ -2179,6 +2186,7 @@ pub(crate) async fn update_environment(
|
|||
spec: RequirementsSpecification,
|
||||
modifications: Modifications,
|
||||
build_constraints: Constraints,
|
||||
extra_build_requires: uv_distribution::ExtraBuildRequires,
|
||||
settings: &ResolverInstallerSettings,
|
||||
network_settings: &NetworkSettings,
|
||||
state: &SharedState,
|
||||
|
|
@ -2209,7 +2217,7 @@ pub(crate) async fn update_environment(
|
|||
link_mode,
|
||||
no_build_isolation,
|
||||
no_build_isolation_package,
|
||||
extra_build_dependencies,
|
||||
extra_build_dependencies: _,
|
||||
prerelease,
|
||||
resolution,
|
||||
sources,
|
||||
|
|
@ -2326,9 +2334,6 @@ pub(crate) async fn update_environment(
|
|||
};
|
||||
|
||||
// Create a build dispatch.
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
extra_build_dependencies.clone(),
|
||||
);
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
cache,
|
||||
|
|
@ -2547,12 +2552,12 @@ pub(crate) fn detect_conflicts(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Determine the [`RequirementsSpecification`] for a script.
|
||||
/// Determine the [`ScriptSpecification`] for a script.
|
||||
#[allow(clippy::result_large_err)]
|
||||
pub(crate) fn script_specification(
|
||||
script: Pep723ItemRef<'_>,
|
||||
settings: &ResolverSettings,
|
||||
) -> Result<Option<RequirementsSpecification>, ProjectError> {
|
||||
) -> Result<Option<ScriptSpecification>, ProjectError> {
|
||||
let Some(dependencies) = script.metadata().dependencies.as_ref() else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
|
@ -2647,11 +2652,47 @@ pub(crate) fn script_specification(
|
|||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
Ok(Some(RequirementsSpecification::from_overrides(
|
||||
requirements,
|
||||
constraints,
|
||||
overrides,
|
||||
)))
|
||||
// Collect any `tool.uv.extra-build-dependencies` from the script.
|
||||
let empty = BTreeMap::default();
|
||||
let script_extra_build_dependencies = script
|
||||
.metadata()
|
||||
.tool
|
||||
.as_ref()
|
||||
.and_then(|tool| tool.uv.as_ref())
|
||||
.and_then(|uv| uv.extra_build_dependencies.as_ref())
|
||||
.unwrap_or(&empty);
|
||||
|
||||
// Lower the extra build dependencies
|
||||
let mut extra_build_dependencies = ExtraBuildDependencies::default();
|
||||
for (name, requirements) in script_extra_build_dependencies {
|
||||
let lowered_requirements: Vec<_> = requirements
|
||||
.iter()
|
||||
.cloned()
|
||||
.flat_map(|requirement| {
|
||||
LoweredRequirement::from_non_workspace_requirement(
|
||||
requirement,
|
||||
script_dir.as_ref(),
|
||||
script_sources,
|
||||
script_indexes,
|
||||
&settings.index_locations,
|
||||
)
|
||||
.map_ok(|req| req.into_inner().into())
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
extra_build_dependencies.insert(name.clone(), lowered_requirements);
|
||||
}
|
||||
|
||||
let extra_build_requires =
|
||||
uv_distribution::ExtraBuildRequires::from_lowered(extra_build_dependencies);
|
||||
|
||||
Ok(Some(ScriptSpecification {
|
||||
requirements: RequirementsSpecification::from_overrides(
|
||||
requirements,
|
||||
constraints,
|
||||
overrides,
|
||||
),
|
||||
extra_build_requires,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Warn if the user provides (e.g.) an `--index-url` in a requirements file.
|
||||
|
|
|
|||
|
|
@ -386,6 +386,7 @@ pub(crate) async fn remove(
|
|||
|
||||
/// Represents the destination where dependencies are added, either to a project or a script.
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum RemoveTarget {
|
||||
/// A PEP 723 script, with inline metadata.
|
||||
Project(VirtualProject),
|
||||
|
|
|
|||
|
|
@ -358,7 +358,9 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
|||
}
|
||||
|
||||
// Install the script requirements, if necessary. Otherwise, use an isolated environment.
|
||||
if let Some(spec) = script_specification((&script).into(), &settings.resolver)? {
|
||||
if let Some(script_spec) = script_specification((&script).into(), &settings.resolver)? {
|
||||
let spec = script_spec.requirements;
|
||||
let script_extra_build_requires = script_spec.extra_build_requires;
|
||||
let environment = ScriptEnvironment::get_or_init(
|
||||
(&script).into(),
|
||||
python.as_deref().map(PythonRequest::parse),
|
||||
|
|
@ -407,6 +409,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
|||
spec,
|
||||
modifications,
|
||||
build_constraints.unwrap_or_default(),
|
||||
script_extra_build_requires,
|
||||
&settings,
|
||||
&network_settings,
|
||||
&sync_state,
|
||||
|
|
|
|||
|
|
@ -14,12 +14,11 @@ use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder};
|
|||
use uv_configuration::{
|
||||
Concurrency, Constraints, DependencyGroups, DependencyGroupsWithDefaults, DryRun, EditableMode,
|
||||
ExtrasSpecification, ExtrasSpecificationWithDefaults, HashCheckingMode, InstallOptions,
|
||||
PreviewMode, TargetTriple,
|
||||
PreviewMode, TargetTriple, Upgrade,
|
||||
};
|
||||
use uv_dispatch::BuildDispatch;
|
||||
use uv_distribution_types::{
|
||||
DirectorySourceDist, Dist, Index, Requirement, Resolution, ResolvedDist,
|
||||
SourceDist,
|
||||
DirectorySourceDist, Dist, Index, Requirement, Resolution, ResolvedDist, SourceDist,
|
||||
};
|
||||
use uv_fs::{PortablePathBuf, Simplified};
|
||||
use uv_installer::SitePackages;
|
||||
|
|
@ -27,15 +26,14 @@ use uv_normalize::{DefaultExtras, DefaultGroups, PackageName};
|
|||
use uv_pep508::{MarkerTree, VersionOrUrl};
|
||||
use uv_pypi_types::{ParsedArchiveUrl, ParsedGitUrl, ParsedUrl};
|
||||
use uv_python::{PythonDownloads, PythonEnvironment, PythonPreference, PythonRequest};
|
||||
use uv_resolver::{FlatIndex, Installable, Lock};
|
||||
use uv_requirements::RequirementsSpecification;
|
||||
use uv_resolver::{FlatIndex, ForkStrategy, Installable, Lock, PrereleaseMode, ResolutionMode};
|
||||
use uv_scripts::{Pep723ItemRef, Pep723Script};
|
||||
use uv_settings::PythonInstallMirrors;
|
||||
use uv_types::{BuildIsolation, HashStrategy};
|
||||
use uv_warnings::{warn_user, warn_user_once};
|
||||
use uv_workspace::pyproject::{ExtraBuildDependencies, Source};
|
||||
use uv_workspace::{
|
||||
DiscoveryOptions, MemberDiscovery, VirtualProject, Workspace, WorkspaceCache,
|
||||
};
|
||||
use uv_workspace::{DiscoveryOptions, MemberDiscovery, VirtualProject, Workspace, WorkspaceCache};
|
||||
|
||||
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger, InstallLogger};
|
||||
use crate::commands::pip::operations::Modifications;
|
||||
|
|
@ -50,7 +48,9 @@ use crate::commands::project::{
|
|||
};
|
||||
use crate::commands::{ExitStatus, diagnostics};
|
||||
use crate::printer::Printer;
|
||||
use crate::settings::{InstallerSettingsRef, NetworkSettings, ResolverInstallerSettings};
|
||||
use crate::settings::{
|
||||
InstallerSettingsRef, NetworkSettings, ResolverInstallerSettings, ResolverSettings,
|
||||
};
|
||||
|
||||
/// Sync the project environment.
|
||||
#[allow(clippy::fn_params_excessive_bools)]
|
||||
|
|
@ -223,8 +223,18 @@ pub(crate) async fn sync(
|
|||
}
|
||||
|
||||
// Parse the requirements from the script.
|
||||
let spec = script_specification(Pep723ItemRef::Script(script), &settings.resolver)?
|
||||
.unwrap_or_default();
|
||||
let script_spec =
|
||||
script_specification(Pep723ItemRef::Script(script), &settings.resolver)?;
|
||||
let (spec, script_extra_build_requires) = if let Some(script_spec) = script_spec {
|
||||
(script_spec.requirements, script_spec.extra_build_requires)
|
||||
} else {
|
||||
(
|
||||
RequirementsSpecification::default(),
|
||||
uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
ExtraBuildDependencies::default(),
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
// Parse the build constraints from the script.
|
||||
let build_constraints = script
|
||||
|
|
@ -249,6 +259,7 @@ pub(crate) async fn sync(
|
|||
spec,
|
||||
modifications,
|
||||
build_constraints.unwrap_or_default(),
|
||||
script_extra_build_requires,
|
||||
&settings,
|
||||
&network_settings,
|
||||
&PlatformState::default(),
|
||||
|
|
@ -495,6 +506,7 @@ fn identify_installation_target<'a>(
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum SyncTarget {
|
||||
/// Sync a project environment.
|
||||
Project(VirtualProject),
|
||||
|
|
@ -546,7 +558,6 @@ impl Deref for SyncEnvironment {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// Sync a lockfile with an environment.
|
||||
#[allow(clippy::fn_params_excessive_bools)]
|
||||
pub(super) async fn do_sync(
|
||||
|
|
@ -607,12 +618,35 @@ pub(super) async fn do_sync(
|
|||
sources,
|
||||
)?
|
||||
}
|
||||
InstallTarget::Script { .. } => uv_distribution::ExtraBuildRequires {
|
||||
extra_build_dependencies: ExtraBuildDependencies::default(),
|
||||
},
|
||||
InstallTarget::Script { script, .. } => {
|
||||
// Try to get extra build dependencies from the script metadata
|
||||
let resolver_settings = ResolverSettings {
|
||||
build_options: build_options.clone(),
|
||||
config_setting: config_setting.clone(),
|
||||
config_settings_package: config_settings_package.clone(),
|
||||
dependency_metadata: dependency_metadata.clone(),
|
||||
exclude_newer,
|
||||
fork_strategy: ForkStrategy::default(),
|
||||
index_locations: index_locations.clone(),
|
||||
index_strategy,
|
||||
keyring_provider,
|
||||
link_mode,
|
||||
no_build_isolation,
|
||||
no_build_isolation_package: no_build_isolation_package.to_vec(),
|
||||
extra_build_dependencies: extra_build_dependencies.clone(),
|
||||
prerelease: PrereleaseMode::default(),
|
||||
resolution: ResolutionMode::default(),
|
||||
sources,
|
||||
upgrade: Upgrade::default(),
|
||||
};
|
||||
script_specification(Pep723ItemRef::Script(script), &resolver_settings)?
|
||||
.map(|spec| spec.extra_build_requires)
|
||||
.unwrap_or_else(|| uv_distribution::ExtraBuildRequires {
|
||||
extra_build_dependencies: ExtraBuildDependencies::default(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let client_builder = BaseClientBuilder::new()
|
||||
.retries_from_env()?
|
||||
.connectivity(network_settings.connectivity)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
|||
use uv_settings::{PythonInstallMirrors, ResolverInstallerOptions, ToolOptions};
|
||||
use uv_tool::InstalledTools;
|
||||
use uv_warnings::warn_user;
|
||||
use uv_workspace::WorkspaceCache;
|
||||
use uv_workspace::{WorkspaceCache, pyproject::ExtraBuildDependencies};
|
||||
|
||||
use crate::commands::ExitStatus;
|
||||
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger};
|
||||
|
|
@ -439,6 +439,7 @@ pub(crate) async fn install(
|
|||
spec,
|
||||
Modifications::Exact,
|
||||
Constraints::from_requirements(build_constraints.iter().cloned()),
|
||||
uv_distribution::ExtraBuildRequires::from_lowered(ExtraBuildDependencies::default()),
|
||||
&settings,
|
||||
&network_settings,
|
||||
&state,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use uv_python::{
|
|||
use uv_requirements::RequirementsSpecification;
|
||||
use uv_settings::{Combine, PythonInstallMirrors, ResolverInstallerOptions, ToolOptions};
|
||||
use uv_tool::InstalledTools;
|
||||
use uv_workspace::WorkspaceCache;
|
||||
use uv_workspace::{WorkspaceCache, pyproject::ExtraBuildDependencies};
|
||||
|
||||
use crate::commands::pip::loggers::{
|
||||
DefaultInstallLogger, SummaryResolveLogger, UpgradeInstallLogger,
|
||||
|
|
@ -343,6 +343,7 @@ async fn upgrade_tool(
|
|||
spec,
|
||||
Modifications::Exact,
|
||||
build_constraints,
|
||||
uv_distribution::ExtraBuildRequires::from_lowered(ExtraBuildDependencies::default()),
|
||||
&settings,
|
||||
network_settings,
|
||||
&state,
|
||||
|
|
|
|||
|
|
@ -275,9 +275,8 @@ pub(crate) async fn venv(
|
|||
|
||||
// Do not allow builds
|
||||
let build_options = BuildOptions::new(NoBinary::None, NoBuild::All);
|
||||
let extra_build_requires = uv_distribution::ExtraBuildRequires::from_lowered(
|
||||
ExtraBuildDependencies::default(),
|
||||
);
|
||||
let extra_build_requires =
|
||||
uv_distribution::ExtraBuildRequires::from_lowered(ExtraBuildDependencies::default());
|
||||
// Prep the build context.
|
||||
let build_dispatch = BuildDispatch::new(
|
||||
&client,
|
||||
|
|
|
|||
|
|
@ -3931,6 +3931,7 @@ fn resolve_both_special_fields() -> anyhow::Result<()> {
|
|||
torch_backend: None,
|
||||
no_build_isolation: false,
|
||||
no_build_isolation_package: [],
|
||||
extra_build_dependencies: {},
|
||||
build_options: BuildOptions {
|
||||
no_binary: None,
|
||||
no_build: None,
|
||||
|
|
|
|||
|
|
@ -1778,7 +1778,7 @@ fn sync_extra_build_dependencies() -> Result<()> {
|
|||
[tool.uv.sources]
|
||||
child = { path = "child" }
|
||||
bad_child = { path = "bad_child" }
|
||||
|
||||
|
||||
[tool.uv.extra-build-dependencies]
|
||||
child = ["anyio"]
|
||||
"#})?;
|
||||
|
|
@ -1833,7 +1833,7 @@ fn sync_extra_build_dependencies_sources() -> Result<()> {
|
|||
except ModuleNotFoundError:
|
||||
print("Missing `anyio` module", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# Check that we got the local version of anyio by checking for the marker
|
||||
if not hasattr(anyio, 'LOCAL_ANYIO_MARKER'):
|
||||
print("Found system anyio instead of local anyio", file=sys.stderr)
|
||||
|
|
@ -1915,7 +1915,7 @@ fn sync_extra_build_dependencies_sources_from_child() -> Result<()> {
|
|||
except ModuleNotFoundError:
|
||||
print("Missing `anyio` module", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# Check that we got the local version of anyio by checking for the marker
|
||||
if not hasattr(anyio, 'LOCAL_ANYIO_MARKER'):
|
||||
print("Found system anyio instead of local anyio", file=sys.stderr)
|
||||
|
|
@ -4593,6 +4593,187 @@ fn no_install_project_no_build() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_extra_build_dependencies_script() -> Result<()> {
|
||||
let context = TestContext::new("3.12").with_filtered_counts();
|
||||
|
||||
// Write a test package that arbitrarily requires `anyio` at build time
|
||||
let child = context.temp_dir.child("child");
|
||||
child.create_dir_all()?;
|
||||
let child_pyproject_toml = child.child("pyproject.toml");
|
||||
child_pyproject_toml.write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "child"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.9"
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
backend-path = ["."]
|
||||
build-backend = "build_backend"
|
||||
"#})?;
|
||||
let build_backend = child.child("build_backend.py");
|
||||
build_backend.write_str(indoc! {r#"
|
||||
import sys
|
||||
from hatchling.build import *
|
||||
try:
|
||||
import anyio
|
||||
except ModuleNotFoundError:
|
||||
print("Missing `anyio` module", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
"#})?;
|
||||
child.child("src/child/__init__.py").touch()?;
|
||||
|
||||
// Create a script that depends on the child package
|
||||
let script = context.temp_dir.child("script.py");
|
||||
script.write_str(indoc! {r#"
|
||||
# /// script
|
||||
# requires-python = ">=3.12"
|
||||
# dependencies = ["child"]
|
||||
#
|
||||
# [tool.uv.sources]
|
||||
# child = { path = "child" }
|
||||
# ///
|
||||
"#})?;
|
||||
|
||||
let filters = context
|
||||
.filters()
|
||||
.into_iter()
|
||||
.chain(vec![(
|
||||
r"environments-v2/script-[a-z0-9]+",
|
||||
"environments-v2/script-[HASH]",
|
||||
)])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Running `uv sync` should fail due to missing build-dependencies
|
||||
uv_snapshot!(filters, context.sync().arg("--script").arg("script.py"), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Creating script environment at: [CACHE_DIR]/environments-v2/script-[HASH]
|
||||
Resolved [N] packages in [TIME]
|
||||
× Failed to build `child @ file://[TEMP_DIR]/child`
|
||||
├─▶ The build backend returned an error
|
||||
╰─▶ Call to `build_backend.build_wheel` failed (exit status: 1)
|
||||
|
||||
[stderr]
|
||||
Missing `anyio` module
|
||||
|
||||
hint: This usually indicates a problem with the package or the build environment.
|
||||
");
|
||||
|
||||
// Add extra build dependencies to the script
|
||||
script.write_str(indoc! {r#"
|
||||
# /// script
|
||||
# requires-python = ">=3.12"
|
||||
# dependencies = ["child"]
|
||||
#
|
||||
# [tool.uv.sources]
|
||||
# child = { path = "child" }
|
||||
#
|
||||
# [tool.uv.extra-build-dependencies]
|
||||
# child = ["anyio"]
|
||||
# ///
|
||||
"#})?;
|
||||
|
||||
// Running `uv sync` should now succeed due to extra build-dependencies
|
||||
context.venv().arg("--clear").assert().success();
|
||||
uv_snapshot!(filters, context.sync().arg("--script").arg("script.py"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Using script environment at: [CACHE_DIR]/environments-v2/script-[HASH]
|
||||
Resolved [N] packages in [TIME]
|
||||
Prepared [N] packages in [TIME]
|
||||
Installed [N] packages in [TIME]
|
||||
+ child==0.1.0 (from file://[TEMP_DIR]/child)
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_extra_build_dependencies_script_sources() -> Result<()> {
|
||||
let context = TestContext::new("3.12").with_filtered_counts();
|
||||
let anyio_local = context.workspace_root.join("scripts/packages/anyio_local");
|
||||
|
||||
// Write a test package that arbitrarily requires `anyio` at a specific _path_ at build time
|
||||
let child = context.temp_dir.child("child");
|
||||
child.create_dir_all()?;
|
||||
let child_pyproject_toml = child.child("pyproject.toml");
|
||||
child_pyproject_toml.write_str(indoc! {r#"
|
||||
[project]
|
||||
name = "child"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.9"
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
backend-path = ["."]
|
||||
build-backend = "build_backend"
|
||||
"#})?;
|
||||
let build_backend = child.child("build_backend.py");
|
||||
build_backend.write_str(&formatdoc! {r#"
|
||||
import sys
|
||||
from hatchling.build import *
|
||||
try:
|
||||
import anyio
|
||||
except ModuleNotFoundError:
|
||||
print("Missing `anyio` module", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Check that we got the local version of anyio by checking for the marker
|
||||
if not hasattr(anyio, 'LOCAL_ANYIO_MARKER'):
|
||||
print("Found system anyio instead of local anyio", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
"#})?;
|
||||
child.child("src/child/__init__.py").touch()?;
|
||||
|
||||
// Create a script that depends on the child package
|
||||
let script = context.temp_dir.child("script.py");
|
||||
script.write_str(&formatdoc! {r#"
|
||||
# /// script
|
||||
# requires-python = ">=3.12"
|
||||
# dependencies = ["child"]
|
||||
#
|
||||
# [tool.uv.sources]
|
||||
# anyio = {{ path = "{}" }}
|
||||
# child = {{ path = "child" }}
|
||||
#
|
||||
# [tool.uv.extra-build-dependencies]
|
||||
# child = ["anyio"]
|
||||
# ///
|
||||
"#, anyio_local.display()
|
||||
})?;
|
||||
|
||||
let filters = context
|
||||
.filters()
|
||||
.into_iter()
|
||||
.chain(vec![(
|
||||
r"environments-v2/script-[a-z0-9]+",
|
||||
"environments-v2/script-[HASH]",
|
||||
)])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Running `uv sync` should succeed with the sources applied
|
||||
uv_snapshot!(filters, context.sync().arg("--script").arg("script.py"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Creating script environment at: [CACHE_DIR]/environments-v2/script-[HASH]
|
||||
Resolved [N] packages in [TIME]
|
||||
Prepared [N] packages in [TIME]
|
||||
Installed [N] packages in [TIME]
|
||||
+ child==0.1.0 (from file://[TEMP_DIR]/child)
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn virtual_no_build() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
|
|
|||
Loading…
Reference in New Issue