mirror of https://github.com/astral-sh/uv
Add `--no-editable` support to `uv sync` and `uv export` (#7371)
## Summary Closes https://github.com/astral-sh/uv/issues/5792.
This commit is contained in:
parent
bb0ffa32e4
commit
778da3350a
|
|
@ -76,15 +76,34 @@ impl Resolution {
|
||||||
pub fn filter(self, predicate: impl Fn(&ResolvedDist) -> bool) -> Self {
|
pub fn filter(self, predicate: impl Fn(&ResolvedDist) -> bool) -> Self {
|
||||||
let packages = self
|
let packages = self
|
||||||
.packages
|
.packages
|
||||||
.iter()
|
.into_iter()
|
||||||
.filter(|(_, dist)| predicate(dist))
|
.filter(|(_, dist)| predicate(dist))
|
||||||
.map(|(name, dist)| (name.clone(), dist.clone()))
|
|
||||||
.collect::<BTreeMap<_, _>>();
|
.collect::<BTreeMap<_, _>>();
|
||||||
let hashes = self
|
let hashes = self
|
||||||
.hashes
|
.hashes
|
||||||
.iter()
|
.into_iter()
|
||||||
|
.filter(|(name, _)| packages.contains_key(name))
|
||||||
|
.collect();
|
||||||
|
let diagnostics = self.diagnostics.clone();
|
||||||
|
Self {
|
||||||
|
packages,
|
||||||
|
hashes,
|
||||||
|
diagnostics,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map over the resolved distributions in this resolution.
|
||||||
|
#[must_use]
|
||||||
|
pub fn map(self, predicate: impl Fn(ResolvedDist) -> ResolvedDist) -> Self {
|
||||||
|
let packages = self
|
||||||
|
.packages
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, dist)| (name, predicate(dist)))
|
||||||
|
.collect::<BTreeMap<_, _>>();
|
||||||
|
let hashes = self
|
||||||
|
.hashes
|
||||||
|
.into_iter()
|
||||||
.filter(|(name, _)| packages.contains_key(name))
|
.filter(|(name, _)| packages.contains_key(name))
|
||||||
.map(|(name, hashes)| (name.clone(), hashes.clone()))
|
|
||||||
.collect();
|
.collect();
|
||||||
let diagnostics = self.diagnostics.clone();
|
let diagnostics = self.diagnostics.clone();
|
||||||
Self {
|
Self {
|
||||||
|
|
|
||||||
|
|
@ -2408,6 +2408,11 @@ pub struct RunArgs {
|
||||||
#[arg(long, conflicts_with("no_dev"))]
|
#[arg(long, conflicts_with("no_dev"))]
|
||||||
pub only_dev: bool,
|
pub only_dev: bool,
|
||||||
|
|
||||||
|
/// Install any editable dependencies, including the project and any workspace members, as
|
||||||
|
/// non-editable.
|
||||||
|
#[arg(long)]
|
||||||
|
pub no_editable: bool,
|
||||||
|
|
||||||
/// The command to run.
|
/// The command to run.
|
||||||
///
|
///
|
||||||
/// If the path to a Python script (i.e., ending in `.py`), it will be
|
/// If the path to a Python script (i.e., ending in `.py`), it will be
|
||||||
|
|
@ -2560,6 +2565,11 @@ pub struct SyncArgs {
|
||||||
#[arg(long, conflicts_with("no_dev"))]
|
#[arg(long, conflicts_with("no_dev"))]
|
||||||
pub only_dev: bool,
|
pub only_dev: bool,
|
||||||
|
|
||||||
|
/// Install any editable dependencies, including the project and any workspace members, as
|
||||||
|
/// non-editable.
|
||||||
|
#[arg(long)]
|
||||||
|
pub no_editable: bool,
|
||||||
|
|
||||||
/// Do not remove extraneous packages present in the environment.
|
/// Do not remove extraneous packages present in the environment.
|
||||||
///
|
///
|
||||||
/// When enabled, uv will make the minimum necessary changes to satisfy the requirements.
|
/// When enabled, uv will make the minimum necessary changes to satisfy the requirements.
|
||||||
|
|
@ -3002,6 +3012,11 @@ pub struct ExportArgs {
|
||||||
#[arg(long, conflicts_with("no_dev"))]
|
#[arg(long, conflicts_with("no_dev"))]
|
||||||
pub only_dev: bool,
|
pub only_dev: bool,
|
||||||
|
|
||||||
|
/// Install any editable dependencies, including the project and any workspace members, as
|
||||||
|
/// non-editable.
|
||||||
|
#[arg(long)]
|
||||||
|
pub no_editable: bool,
|
||||||
|
|
||||||
/// Include hashes for all dependencies.
|
/// Include hashes for all dependencies.
|
||||||
#[arg(long, overrides_with("no_hashes"), hide = true)]
|
#[arg(long, overrides_with("no_hashes"), hide = true)]
|
||||||
pub hashes: bool,
|
pub hashes: bool,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum EditableMode {
|
||||||
|
#[default]
|
||||||
|
Editable,
|
||||||
|
NonEditable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EditableMode {
|
||||||
|
/// Determine the editable mode based on the command-line arguments.
|
||||||
|
pub fn from_args(no_editable: bool) -> Self {
|
||||||
|
if no_editable {
|
||||||
|
Self::NonEditable
|
||||||
|
} else {
|
||||||
|
Self::Editable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ pub use concurrency::*;
|
||||||
pub use config_settings::*;
|
pub use config_settings::*;
|
||||||
pub use constraints::*;
|
pub use constraints::*;
|
||||||
pub use dev::*;
|
pub use dev::*;
|
||||||
|
pub use editable::*;
|
||||||
pub use export_format::*;
|
pub use export_format::*;
|
||||||
pub use extras::*;
|
pub use extras::*;
|
||||||
pub use hash::*;
|
pub use hash::*;
|
||||||
|
|
@ -22,6 +23,7 @@ mod concurrency;
|
||||||
mod config_settings;
|
mod config_settings;
|
||||||
mod constraints;
|
mod constraints;
|
||||||
mod dev;
|
mod dev;
|
||||||
|
mod editable;
|
||||||
mod export_format;
|
mod export_format;
|
||||||
mod extras;
|
mod extras;
|
||||||
mod hash;
|
mod hash;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ use url::Url;
|
||||||
use distribution_filename::{DistExtension, SourceDistExtension};
|
use distribution_filename::{DistExtension, SourceDistExtension};
|
||||||
use pep508_rs::MarkerTree;
|
use pep508_rs::MarkerTree;
|
||||||
use pypi_types::{ParsedArchiveUrl, ParsedGitUrl};
|
use pypi_types::{ParsedArchiveUrl, ParsedGitUrl};
|
||||||
use uv_configuration::{DevSpecification, ExtrasSpecification, InstallOptions};
|
use uv_configuration::{DevSpecification, EditableMode, ExtrasSpecification, InstallOptions};
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
use uv_git::GitReference;
|
use uv_git::GitReference;
|
||||||
use uv_normalize::{ExtraName, PackageName};
|
use uv_normalize::{ExtraName, PackageName};
|
||||||
|
|
@ -35,6 +35,7 @@ struct Node<'lock> {
|
||||||
pub struct RequirementsTxtExport<'lock> {
|
pub struct RequirementsTxtExport<'lock> {
|
||||||
nodes: Vec<Node<'lock>>,
|
nodes: Vec<Node<'lock>>,
|
||||||
hashes: bool,
|
hashes: bool,
|
||||||
|
editable: EditableMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lock> RequirementsTxtExport<'lock> {
|
impl<'lock> RequirementsTxtExport<'lock> {
|
||||||
|
|
@ -43,6 +44,7 @@ impl<'lock> RequirementsTxtExport<'lock> {
|
||||||
root_name: &PackageName,
|
root_name: &PackageName,
|
||||||
extras: &ExtrasSpecification,
|
extras: &ExtrasSpecification,
|
||||||
dev: DevSpecification<'_>,
|
dev: DevSpecification<'_>,
|
||||||
|
editable: EditableMode,
|
||||||
hashes: bool,
|
hashes: bool,
|
||||||
install_options: &'lock InstallOptions,
|
install_options: &'lock InstallOptions,
|
||||||
) -> Result<Self, LockError> {
|
) -> Result<Self, LockError> {
|
||||||
|
|
@ -166,7 +168,11 @@ impl<'lock> RequirementsTxtExport<'lock> {
|
||||||
NodeComparator::from(a.package).cmp(&NodeComparator::from(b.package))
|
NodeComparator::from(a.package).cmp(&NodeComparator::from(b.package))
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Self { nodes, hashes })
|
Ok(Self {
|
||||||
|
nodes,
|
||||||
|
hashes,
|
||||||
|
editable,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -216,9 +222,18 @@ impl std::fmt::Display for RequirementsTxtExport<'_> {
|
||||||
write!(f, "{}", anchor(path).portable_display())?;
|
write!(f, "{}", anchor(path).portable_display())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Source::Editable(path) => {
|
Source::Editable(path) => match self.editable {
|
||||||
write!(f, "-e {}", anchor(path).portable_display())?;
|
EditableMode::Editable => {
|
||||||
}
|
write!(f, "-e {}", anchor(path).portable_display())?;
|
||||||
|
}
|
||||||
|
EditableMode::NonEditable => {
|
||||||
|
if path.is_absolute() {
|
||||||
|
write!(f, "{}", Url::from_file_path(path).unwrap())?;
|
||||||
|
} else {
|
||||||
|
write!(f, "{}", anchor(path).portable_display())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
Source::Virtual(_) => {
|
Source::Virtual(_) => {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ use uv_auth::{store_credentials_from_url, Credentials};
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
|
use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
|
||||||
use uv_configuration::{
|
use uv_configuration::{
|
||||||
Concurrency, Constraints, DevMode, ExtrasSpecification, InstallOptions, SourceStrategy,
|
Concurrency, Constraints, DevMode, EditableMode, ExtrasSpecification, InstallOptions,
|
||||||
|
SourceStrategy,
|
||||||
};
|
};
|
||||||
use uv_dispatch::BuildDispatch;
|
use uv_dispatch::BuildDispatch;
|
||||||
use uv_distribution::DistributionDatabase;
|
use uv_distribution::DistributionDatabase;
|
||||||
|
|
@ -755,6 +756,7 @@ async fn lock_and_sync(
|
||||||
&lock,
|
&lock,
|
||||||
&extras,
|
&extras,
|
||||||
dev,
|
dev,
|
||||||
|
EditableMode::Editable,
|
||||||
InstallOptions::default(),
|
InstallOptions::default(),
|
||||||
Modifications::Sufficient,
|
Modifications::Sufficient,
|
||||||
settings.into(),
|
settings.into(),
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ use std::path::PathBuf;
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::Connectivity;
|
use uv_client::Connectivity;
|
||||||
use uv_configuration::{
|
use uv_configuration::{
|
||||||
Concurrency, DevMode, DevSpecification, ExportFormat, ExtrasSpecification, InstallOptions,
|
Concurrency, DevMode, DevSpecification, EditableMode, ExportFormat, ExtrasSpecification,
|
||||||
|
InstallOptions,
|
||||||
};
|
};
|
||||||
use uv_fs::CWD;
|
use uv_fs::CWD;
|
||||||
use uv_normalize::{PackageName, DEV_DEPENDENCIES};
|
use uv_normalize::{PackageName, DEV_DEPENDENCIES};
|
||||||
|
|
@ -33,6 +34,7 @@ pub(crate) async fn export(
|
||||||
output_file: Option<PathBuf>,
|
output_file: Option<PathBuf>,
|
||||||
extras: ExtrasSpecification,
|
extras: ExtrasSpecification,
|
||||||
dev: DevMode,
|
dev: DevMode,
|
||||||
|
editable: EditableMode,
|
||||||
locked: bool,
|
locked: bool,
|
||||||
frozen: bool,
|
frozen: bool,
|
||||||
python: Option<String>,
|
python: Option<String>,
|
||||||
|
|
@ -130,6 +132,7 @@ pub(crate) async fn export(
|
||||||
project.project_name(),
|
project.project_name(),
|
||||||
&extras,
|
&extras,
|
||||||
dev,
|
dev,
|
||||||
|
editable,
|
||||||
hashes,
|
hashes,
|
||||||
&install_options,
|
&install_options,
|
||||||
)?;
|
)?;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use owo_colors::OwoColorize;
|
||||||
use pep508_rs::PackageName;
|
use pep508_rs::PackageName;
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::Connectivity;
|
use uv_client::Connectivity;
|
||||||
use uv_configuration::{Concurrency, DevMode, ExtrasSpecification, InstallOptions};
|
use uv_configuration::{Concurrency, DevMode, EditableMode, ExtrasSpecification, InstallOptions};
|
||||||
use uv_fs::{Simplified, CWD};
|
use uv_fs::{Simplified, CWD};
|
||||||
use uv_python::{PythonDownloads, PythonPreference, PythonRequest};
|
use uv_python::{PythonDownloads, PythonPreference, PythonRequest};
|
||||||
use uv_scripts::Pep723Script;
|
use uv_scripts::Pep723Script;
|
||||||
|
|
@ -188,8 +188,8 @@ pub(crate) async fn remove(
|
||||||
|
|
||||||
// Perform a full sync, because we don't know what exactly is affected by the removal.
|
// Perform a full sync, because we don't know what exactly is affected by the removal.
|
||||||
// TODO(ibraheem): Should we accept CLI overrides for this? Should we even sync here?
|
// TODO(ibraheem): Should we accept CLI overrides for this? Should we even sync here?
|
||||||
let extras = ExtrasSpecification::All;
|
|
||||||
let dev = DevMode::Include;
|
let dev = DevMode::Include;
|
||||||
|
let extras = ExtrasSpecification::All;
|
||||||
let install_options = InstallOptions::default();
|
let install_options = InstallOptions::default();
|
||||||
|
|
||||||
// Initialize any shared state.
|
// Initialize any shared state.
|
||||||
|
|
@ -201,6 +201,7 @@ pub(crate) async fn remove(
|
||||||
&lock,
|
&lock,
|
||||||
&extras,
|
&extras,
|
||||||
dev,
|
dev,
|
||||||
|
EditableMode::Editable,
|
||||||
install_options,
|
install_options,
|
||||||
Modifications::Exact,
|
Modifications::Exact,
|
||||||
settings.as_ref().into(),
|
settings.as_ref().into(),
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,9 @@ use tracing::{debug, warn};
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_cli::ExternalCommand;
|
use uv_cli::ExternalCommand;
|
||||||
use uv_client::{BaseClientBuilder, Connectivity};
|
use uv_client::{BaseClientBuilder, Connectivity};
|
||||||
use uv_configuration::{Concurrency, DevMode, ExtrasSpecification, InstallOptions, SourceStrategy};
|
use uv_configuration::{
|
||||||
|
Concurrency, DevMode, EditableMode, ExtrasSpecification, InstallOptions, SourceStrategy,
|
||||||
|
};
|
||||||
use uv_distribution::LoweredRequirement;
|
use uv_distribution::LoweredRequirement;
|
||||||
use uv_fs::{PythonExt, Simplified, CWD};
|
use uv_fs::{PythonExt, Simplified, CWD};
|
||||||
use uv_installer::{SatisfiesResult, SitePackages};
|
use uv_installer::{SatisfiesResult, SitePackages};
|
||||||
|
|
@ -58,6 +60,7 @@ pub(crate) async fn run(
|
||||||
no_config: bool,
|
no_config: bool,
|
||||||
extras: ExtrasSpecification,
|
extras: ExtrasSpecification,
|
||||||
dev: DevMode,
|
dev: DevMode,
|
||||||
|
editable: EditableMode,
|
||||||
python: Option<String>,
|
python: Option<String>,
|
||||||
settings: ResolverInstallerSettings,
|
settings: ResolverInstallerSettings,
|
||||||
python_preference: PythonPreference,
|
python_preference: PythonPreference,
|
||||||
|
|
@ -501,6 +504,7 @@ pub(crate) async fn run(
|
||||||
result.lock(),
|
result.lock(),
|
||||||
&extras,
|
&extras,
|
||||||
dev,
|
dev,
|
||||||
|
editable,
|
||||||
install_options,
|
install_options,
|
||||||
Modifications::Sufficient,
|
Modifications::Sufficient,
|
||||||
settings.as_ref().into(),
|
settings.as_ref().into(),
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use distribution_types::{Dist, ResolvedDist, SourceDist};
|
use distribution_types::{DirectorySourceDist, Dist, ResolvedDist, SourceDist};
|
||||||
use pep508_rs::MarkerTree;
|
use pep508_rs::MarkerTree;
|
||||||
use uv_auth::store_credentials_from_url;
|
use uv_auth::store_credentials_from_url;
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::{Connectivity, FlatIndexClient, RegistryClientBuilder};
|
use uv_client::{Connectivity, FlatIndexClient, RegistryClientBuilder};
|
||||||
use uv_configuration::{
|
use uv_configuration::{
|
||||||
Concurrency, Constraints, DevMode, DevSpecification, ExtrasSpecification, HashCheckingMode,
|
Concurrency, Constraints, DevMode, DevSpecification, EditableMode, ExtrasSpecification,
|
||||||
InstallOptions,
|
HashCheckingMode, InstallOptions,
|
||||||
};
|
};
|
||||||
use uv_dispatch::BuildDispatch;
|
use uv_dispatch::BuildDispatch;
|
||||||
use uv_fs::CWD;
|
use uv_fs::CWD;
|
||||||
|
|
@ -36,6 +36,7 @@ pub(crate) async fn sync(
|
||||||
package: Option<PackageName>,
|
package: Option<PackageName>,
|
||||||
extras: ExtrasSpecification,
|
extras: ExtrasSpecification,
|
||||||
dev: DevMode,
|
dev: DevMode,
|
||||||
|
editable: EditableMode,
|
||||||
install_options: InstallOptions,
|
install_options: InstallOptions,
|
||||||
modifications: Modifications,
|
modifications: Modifications,
|
||||||
python: Option<String>,
|
python: Option<String>,
|
||||||
|
|
@ -133,6 +134,7 @@ pub(crate) async fn sync(
|
||||||
&lock,
|
&lock,
|
||||||
&extras,
|
&extras,
|
||||||
dev,
|
dev,
|
||||||
|
editable,
|
||||||
install_options,
|
install_options,
|
||||||
modifications,
|
modifications,
|
||||||
settings.as_ref().into(),
|
settings.as_ref().into(),
|
||||||
|
|
@ -157,6 +159,7 @@ pub(super) async fn do_sync(
|
||||||
lock: &Lock,
|
lock: &Lock,
|
||||||
extras: &ExtrasSpecification,
|
extras: &ExtrasSpecification,
|
||||||
dev: DevMode,
|
dev: DevMode,
|
||||||
|
editable: EditableMode,
|
||||||
install_options: InstallOptions,
|
install_options: InstallOptions,
|
||||||
modifications: Modifications,
|
modifications: Modifications,
|
||||||
settings: InstallerSettingsRef<'_>,
|
settings: InstallerSettingsRef<'_>,
|
||||||
|
|
@ -242,6 +245,9 @@ pub(super) async fn do_sync(
|
||||||
// Always skip virtual projects, which shouldn't be built or installed.
|
// Always skip virtual projects, which shouldn't be built or installed.
|
||||||
let resolution = apply_no_virtual_project(resolution);
|
let resolution = apply_no_virtual_project(resolution);
|
||||||
|
|
||||||
|
// If necessary, convert editable to non-editable distributions.
|
||||||
|
let resolution = apply_editable_mode(resolution, editable);
|
||||||
|
|
||||||
// Add all authenticated sources to the cache.
|
// Add all authenticated sources to the cache.
|
||||||
for url in index_locations.urls() {
|
for url in index_locations.urls() {
|
||||||
store_credentials_from_url(url);
|
store_credentials_from_url(url);
|
||||||
|
|
@ -358,3 +364,38 @@ fn apply_no_virtual_project(
|
||||||
!dist.r#virtual
|
!dist.r#virtual
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If necessary, convert any editable requirements to non-editable.
|
||||||
|
fn apply_editable_mode(
|
||||||
|
resolution: distribution_types::Resolution,
|
||||||
|
editable: EditableMode,
|
||||||
|
) -> distribution_types::Resolution {
|
||||||
|
match editable {
|
||||||
|
// No modifications are necessary for editable mode; retain any editable distributions.
|
||||||
|
EditableMode::Editable => resolution,
|
||||||
|
|
||||||
|
// Filter out any editable distributions.
|
||||||
|
EditableMode::NonEditable => resolution.map(|dist| {
|
||||||
|
let ResolvedDist::Installable(Dist::Source(SourceDist::Directory(
|
||||||
|
DirectorySourceDist {
|
||||||
|
name,
|
||||||
|
install_path,
|
||||||
|
editable: true,
|
||||||
|
r#virtual: false,
|
||||||
|
url,
|
||||||
|
},
|
||||||
|
))) = dist
|
||||||
|
else {
|
||||||
|
return dist;
|
||||||
|
};
|
||||||
|
|
||||||
|
ResolvedDist::Installable(Dist::Source(SourceDist::Directory(DirectorySourceDist {
|
||||||
|
name,
|
||||||
|
install_path,
|
||||||
|
editable: false,
|
||||||
|
r#virtual: false,
|
||||||
|
url,
|
||||||
|
})))
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1157,6 +1157,7 @@ async fn run_project(
|
||||||
no_config,
|
no_config,
|
||||||
args.extras,
|
args.extras,
|
||||||
args.dev,
|
args.dev,
|
||||||
|
args.editable,
|
||||||
args.python,
|
args.python,
|
||||||
args.settings,
|
args.settings,
|
||||||
globals.python_preference,
|
globals.python_preference,
|
||||||
|
|
@ -1187,6 +1188,7 @@ async fn run_project(
|
||||||
args.package,
|
args.package,
|
||||||
args.extras,
|
args.extras,
|
||||||
args.dev,
|
args.dev,
|
||||||
|
args.editable,
|
||||||
args.install_options,
|
args.install_options,
|
||||||
args.modifications,
|
args.modifications,
|
||||||
args.python,
|
args.python,
|
||||||
|
|
@ -1355,6 +1357,7 @@ async fn run_project(
|
||||||
args.output_file,
|
args.output_file,
|
||||||
args.extras,
|
args.extras,
|
||||||
args.dev,
|
args.dev,
|
||||||
|
args.editable,
|
||||||
args.locked,
|
args.locked,
|
||||||
args.frozen,
|
args.frozen,
|
||||||
args.python,
|
args.python,
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ use uv_cli::{
|
||||||
};
|
};
|
||||||
use uv_client::Connectivity;
|
use uv_client::Connectivity;
|
||||||
use uv_configuration::{
|
use uv_configuration::{
|
||||||
BuildOptions, Concurrency, ConfigSettings, DevMode, ExportFormat, ExtrasSpecification,
|
BuildOptions, Concurrency, ConfigSettings, DevMode, EditableMode, ExportFormat,
|
||||||
HashCheckingMode, IndexStrategy, InstallOptions, KeyringProviderType, NoBinary, NoBuild,
|
ExtrasSpecification, HashCheckingMode, IndexStrategy, InstallOptions, KeyringProviderType,
|
||||||
PreviewMode, Reinstall, SourceStrategy, TargetTriple, TrustedHost, Upgrade,
|
NoBinary, NoBuild, PreviewMode, Reinstall, SourceStrategy, TargetTriple, TrustedHost, Upgrade,
|
||||||
};
|
};
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_python::{Prefix, PythonDownloads, PythonPreference, PythonVersion, Target};
|
use uv_python::{Prefix, PythonDownloads, PythonPreference, PythonVersion, Target};
|
||||||
|
|
@ -210,6 +210,7 @@ pub(crate) struct RunSettings {
|
||||||
pub(crate) frozen: bool,
|
pub(crate) frozen: bool,
|
||||||
pub(crate) extras: ExtrasSpecification,
|
pub(crate) extras: ExtrasSpecification,
|
||||||
pub(crate) dev: DevMode,
|
pub(crate) dev: DevMode,
|
||||||
|
pub(crate) editable: EditableMode,
|
||||||
pub(crate) with: Vec<String>,
|
pub(crate) with: Vec<String>,
|
||||||
pub(crate) with_editable: Vec<String>,
|
pub(crate) with_editable: Vec<String>,
|
||||||
pub(crate) with_requirements: Vec<PathBuf>,
|
pub(crate) with_requirements: Vec<PathBuf>,
|
||||||
|
|
@ -234,6 +235,7 @@ impl RunSettings {
|
||||||
dev,
|
dev,
|
||||||
no_dev,
|
no_dev,
|
||||||
only_dev,
|
only_dev,
|
||||||
|
no_editable,
|
||||||
command: _,
|
command: _,
|
||||||
with,
|
with,
|
||||||
with_editable,
|
with_editable,
|
||||||
|
|
@ -259,6 +261,7 @@ impl RunSettings {
|
||||||
extra.unwrap_or_default(),
|
extra.unwrap_or_default(),
|
||||||
),
|
),
|
||||||
dev: DevMode::from_args(dev, no_dev, only_dev),
|
dev: DevMode::from_args(dev, no_dev, only_dev),
|
||||||
|
editable: EditableMode::from_args(no_editable),
|
||||||
with,
|
with,
|
||||||
with_editable,
|
with_editable,
|
||||||
with_requirements: with_requirements
|
with_requirements: with_requirements
|
||||||
|
|
@ -661,6 +664,7 @@ pub(crate) struct SyncSettings {
|
||||||
pub(crate) frozen: bool,
|
pub(crate) frozen: bool,
|
||||||
pub(crate) extras: ExtrasSpecification,
|
pub(crate) extras: ExtrasSpecification,
|
||||||
pub(crate) dev: DevMode,
|
pub(crate) dev: DevMode,
|
||||||
|
pub(crate) editable: EditableMode,
|
||||||
pub(crate) install_options: InstallOptions,
|
pub(crate) install_options: InstallOptions,
|
||||||
pub(crate) modifications: Modifications,
|
pub(crate) modifications: Modifications,
|
||||||
pub(crate) package: Option<PackageName>,
|
pub(crate) package: Option<PackageName>,
|
||||||
|
|
@ -680,6 +684,7 @@ impl SyncSettings {
|
||||||
dev,
|
dev,
|
||||||
no_dev,
|
no_dev,
|
||||||
only_dev,
|
only_dev,
|
||||||
|
no_editable,
|
||||||
inexact,
|
inexact,
|
||||||
exact,
|
exact,
|
||||||
no_install_project,
|
no_install_project,
|
||||||
|
|
@ -707,6 +712,7 @@ impl SyncSettings {
|
||||||
extra.unwrap_or_default(),
|
extra.unwrap_or_default(),
|
||||||
),
|
),
|
||||||
dev: DevMode::from_args(dev, no_dev, only_dev),
|
dev: DevMode::from_args(dev, no_dev, only_dev),
|
||||||
|
editable: EditableMode::from_args(no_editable),
|
||||||
install_options: InstallOptions::new(
|
install_options: InstallOptions::new(
|
||||||
no_install_project,
|
no_install_project,
|
||||||
no_install_workspace,
|
no_install_workspace,
|
||||||
|
|
@ -961,6 +967,7 @@ pub(crate) struct ExportSettings {
|
||||||
pub(crate) package: Option<PackageName>,
|
pub(crate) package: Option<PackageName>,
|
||||||
pub(crate) extras: ExtrasSpecification,
|
pub(crate) extras: ExtrasSpecification,
|
||||||
pub(crate) dev: DevMode,
|
pub(crate) dev: DevMode,
|
||||||
|
pub(crate) editable: EditableMode,
|
||||||
pub(crate) hashes: bool,
|
pub(crate) hashes: bool,
|
||||||
pub(crate) install_options: InstallOptions,
|
pub(crate) install_options: InstallOptions,
|
||||||
pub(crate) output_file: Option<PathBuf>,
|
pub(crate) output_file: Option<PathBuf>,
|
||||||
|
|
@ -984,6 +991,7 @@ impl ExportSettings {
|
||||||
dev,
|
dev,
|
||||||
no_dev,
|
no_dev,
|
||||||
only_dev,
|
only_dev,
|
||||||
|
no_editable,
|
||||||
hashes,
|
hashes,
|
||||||
no_hashes,
|
no_hashes,
|
||||||
output_file,
|
output_file,
|
||||||
|
|
@ -1006,6 +1014,7 @@ impl ExportSettings {
|
||||||
extra.unwrap_or_default(),
|
extra.unwrap_or_default(),
|
||||||
),
|
),
|
||||||
dev: DevMode::from_args(dev, no_dev, only_dev),
|
dev: DevMode::from_args(dev, no_dev, only_dev),
|
||||||
|
editable: EditableMode::from_args(no_editable),
|
||||||
hashes: flag(hashes, no_hashes).unwrap_or(true),
|
hashes: flag(hashes, no_hashes).unwrap_or(true),
|
||||||
install_options: InstallOptions::new(
|
install_options: InstallOptions::new(
|
||||||
no_emit_project,
|
no_emit_project,
|
||||||
|
|
|
||||||
|
|
@ -905,3 +905,73 @@ fn no_emit() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_editable() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(
|
||||||
|
r#"
|
||||||
|
[project]
|
||||||
|
name = "project"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = ["anyio==3.7.0", "child"]
|
||||||
|
|
||||||
|
[tool.uv.workspace]
|
||||||
|
members = ["child"]
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
child = { workspace = true }
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let child = context.temp_dir.child("child");
|
||||||
|
child.child("pyproject.toml").write_str(
|
||||||
|
r#"
|
||||||
|
[project]
|
||||||
|
name = "child"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = ["iniconfig>=2"]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
context.lock().assert().success();
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.export().arg("--no-editable"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
# This file was autogenerated by uv via the following command:
|
||||||
|
# uv export --cache-dir [CACHE_DIR] --no-editable
|
||||||
|
.
|
||||||
|
./child
|
||||||
|
anyio==3.7.0 \
|
||||||
|
--hash=sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce \
|
||||||
|
--hash=sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0
|
||||||
|
idna==3.6 \
|
||||||
|
--hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
|
||||||
|
--hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
|
||||||
|
iniconfig==2.0.0 \
|
||||||
|
--hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \
|
||||||
|
--hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374
|
||||||
|
sniffio==1.3.1 \
|
||||||
|
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc \
|
||||||
|
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 6 packages in [TIME]
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2392,6 +2392,89 @@ fn transitive_dev() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Avoid installing dev dependencies of transitive dependencies.
|
||||||
|
#[test]
|
||||||
|
fn sync_no_editable() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(
|
||||||
|
r#"
|
||||||
|
[project]
|
||||||
|
name = "root"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = ["child"]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
child = { workspace = true }
|
||||||
|
|
||||||
|
[tool.uv.workspace]
|
||||||
|
members = ["child"]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let src = context.temp_dir.child("src").child("albatross");
|
||||||
|
src.create_dir_all()?;
|
||||||
|
|
||||||
|
let init = src.child("__init__.py");
|
||||||
|
init.touch()?;
|
||||||
|
|
||||||
|
let child = context.temp_dir.child("child");
|
||||||
|
fs_err::create_dir_all(&child)?;
|
||||||
|
|
||||||
|
let pyproject_toml = child.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(
|
||||||
|
r#"
|
||||||
|
[project]
|
||||||
|
name = "child"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let src = child.child("src").child("child");
|
||||||
|
src.create_dir_all()?;
|
||||||
|
|
||||||
|
let init = src.child("__init__.py");
|
||||||
|
init.touch()?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.sync().arg("--no-editable"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
Prepared 2 packages in [TIME]
|
||||||
|
Installed 2 packages in [TIME]
|
||||||
|
+ child==0.1.0 (from file://[TEMP_DIR]/child)
|
||||||
|
+ root==0.1.0 (from file://[TEMP_DIR]/)
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Remove the project.
|
||||||
|
fs_err::remove_dir_all(&child)?;
|
||||||
|
|
||||||
|
// Ensure that we can still import it.
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--no-sync").arg("python").arg("-c").arg("import child"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
/// Check warning message for <https://github.com/astral-sh/uv/issues/6998>
|
/// Check warning message for <https://github.com/astral-sh/uv/issues/6998>
|
||||||
/// if no `build-system` section is defined.
|
/// if no `build-system` section is defined.
|
||||||
|
|
|
||||||
|
|
@ -67,9 +67,9 @@ project should be packaged and installed.
|
||||||
uv uses the presence of a build system to determine if a project contains a package that should be
|
uv uses the presence of a build system to determine if a project contains a package that should be
|
||||||
installed in the project virtual environment. If a build system is not defined, uv will not attempt
|
installed in the project virtual environment. If a build system is not defined, uv will not attempt
|
||||||
to build or install the project itself, just its dependencies. If a build system is defined, uv will
|
to build or install the project itself, just its dependencies. If a build system is defined, uv will
|
||||||
build and install the project into the project environment. Projects are installed in
|
build and install the project into the project environment. By default, projects are installed in
|
||||||
[editable mode](https://setuptools.pypa.io/en/latest/userguide/development_mode.html) so changes to
|
[editable mode](https://setuptools.pypa.io/en/latest/userguide/development_mode.html) so changes to
|
||||||
the source code are reflected immediately, without reinstallation.
|
the source code are reflected immediately, without re-installation.
|
||||||
|
|
||||||
### Configuring project packaging
|
### Configuring project packaging
|
||||||
|
|
||||||
|
|
@ -297,6 +297,12 @@ use [`uvx`](../guides/tools.md) or
|
||||||
managed = false
|
managed = false
|
||||||
```
|
```
|
||||||
|
|
||||||
|
By default, the project will be installed in editable mode, such that changes to the source code are
|
||||||
|
immediately reflected in the environment. `uv sync` and `uv run` both accept a `--no-editable` flag,
|
||||||
|
which instructs uv to install the project in non-editable mode. `--no-editable` is intended for
|
||||||
|
deployment use-cases, such as building a Docker container, in which the project should be included
|
||||||
|
in the deployed environment without a dependency on the originating source code.
|
||||||
|
|
||||||
### Configuring the project environment path
|
### Configuring the project environment path
|
||||||
|
|
||||||
The `UV_PROJECT_ENVIRONMENT` environment variable can be used to configure the project virtual
|
The `UV_PROJECT_ENVIRONMENT` environment variable can be used to configure the project virtual
|
||||||
|
|
|
||||||
|
|
@ -360,6 +360,50 @@ _contents_ are not copied into the image until the final `uv sync` command.
|
||||||
|
|
||||||
If you want to remove specific packages from the sync, use `--no-install-package <name>`.
|
If you want to remove specific packages from the sync, use `--no-install-package <name>`.
|
||||||
|
|
||||||
|
### Non-editable installs
|
||||||
|
|
||||||
|
By default, uv installs projects and workspace members in editable mode, such that changes to the
|
||||||
|
source code are immediately reflected in the environment.
|
||||||
|
|
||||||
|
`uv sync` and `uv run` both accept a `--no-editable` flag, which instructs uv to install the project
|
||||||
|
in non-editable mode, removing any dependency on the source code.
|
||||||
|
|
||||||
|
In the context of a multi-stage Docker image, `--no-editable` can be used to include the project in
|
||||||
|
the synced virtual environment from one stage, then copy the virtual environment alone (and not the
|
||||||
|
source code) into the final image.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```dockerfile title="Dockerfile"
|
||||||
|
# Install uv
|
||||||
|
FROM python:3.12-slim AS builder
|
||||||
|
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
|
||||||
|
|
||||||
|
# Change the working directory to the `app` directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
|
--mount=type=bind,source=uv.lock,target=uv.lock \
|
||||||
|
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||||
|
uv sync --frozen --no-install-project --no-editable
|
||||||
|
|
||||||
|
# Copy the project into the intermediate image
|
||||||
|
ADD . /app
|
||||||
|
|
||||||
|
# Sync the project
|
||||||
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
|
uv sync --frozen --no-editable
|
||||||
|
|
||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
# Copy the environment, but not the source code
|
||||||
|
COPY --from=builder --chown=app:app /app/.venv /app/.venv
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
CMD ["/app/.venv/bin/hello"]
|
||||||
|
```
|
||||||
|
|
||||||
### Using uv temporarily
|
### Using uv temporarily
|
||||||
|
|
||||||
If uv isn't needed in the final image, the binary can be mounted in each invocation:
|
If uv isn't needed in the final image, the binary can be mounted in each invocation:
|
||||||
|
|
|
||||||
|
|
@ -249,6 +249,8 @@ uv run [OPTIONS] <COMMAND>
|
||||||
|
|
||||||
<p>This option is only available when running in a project.</p>
|
<p>This option is only available when running in a project.</p>
|
||||||
|
|
||||||
|
</dd><dt><code>--no-editable</code></dt><dd><p>Install any editable dependencies, including the project and any workspace members, as non-editable</p>
|
||||||
|
|
||||||
</dd><dt><code>--no-index</code></dt><dd><p>Ignore the registry index (e.g., PyPI), instead relying on direct URL dependencies and those provided via <code>--find-links</code></p>
|
</dd><dt><code>--no-index</code></dt><dd><p>Ignore the registry index (e.g., PyPI), instead relying on direct URL dependencies and those provided via <code>--find-links</code></p>
|
||||||
|
|
||||||
</dd><dt><code>--no-progress</code></dt><dd><p>Hide all progress outputs.</p>
|
</dd><dt><code>--no-progress</code></dt><dd><p>Hide all progress outputs.</p>
|
||||||
|
|
@ -1313,6 +1315,8 @@ uv sync [OPTIONS]
|
||||||
<p>May also be set with the <code>UV_NO_CONFIG</code> environment variable.</p>
|
<p>May also be set with the <code>UV_NO_CONFIG</code> environment variable.</p>
|
||||||
</dd><dt><code>--no-dev</code></dt><dd><p>Omit development dependencies</p>
|
</dd><dt><code>--no-dev</code></dt><dd><p>Omit development dependencies</p>
|
||||||
|
|
||||||
|
</dd><dt><code>--no-editable</code></dt><dd><p>Install any editable dependencies, including the project and any workspace members, as non-editable</p>
|
||||||
|
|
||||||
</dd><dt><code>--no-index</code></dt><dd><p>Ignore the registry index (e.g., PyPI), instead relying on direct URL dependencies and those provided via <code>--find-links</code></p>
|
</dd><dt><code>--no-index</code></dt><dd><p>Ignore the registry index (e.g., PyPI), instead relying on direct URL dependencies and those provided via <code>--find-links</code></p>
|
||||||
|
|
||||||
</dd><dt><code>--no-install-package</code> <i>no-install-package</i></dt><dd><p>Do not install the given package(s).</p>
|
</dd><dt><code>--no-install-package</code> <i>no-install-package</i></dt><dd><p>Do not install the given package(s).</p>
|
||||||
|
|
@ -1869,6 +1873,8 @@ uv export [OPTIONS]
|
||||||
<p>May also be set with the <code>UV_NO_CONFIG</code> environment variable.</p>
|
<p>May also be set with the <code>UV_NO_CONFIG</code> environment variable.</p>
|
||||||
</dd><dt><code>--no-dev</code></dt><dd><p>Omit development dependencies</p>
|
</dd><dt><code>--no-dev</code></dt><dd><p>Omit development dependencies</p>
|
||||||
|
|
||||||
|
</dd><dt><code>--no-editable</code></dt><dd><p>Install any editable dependencies, including the project and any workspace members, as non-editable</p>
|
||||||
|
|
||||||
</dd><dt><code>--no-emit-package</code> <i>no-emit-package</i></dt><dd><p>Do not emit the given package(s).</p>
|
</dd><dt><code>--no-emit-package</code> <i>no-emit-package</i></dt><dd><p>Do not emit the given package(s).</p>
|
||||||
|
|
||||||
<p>By default, all of the project’s dependencies are included in the exported requirements file. The <code>--no-install-package</code> option allows exclusion of specific packages.</p>
|
<p>By default, all of the project’s dependencies are included in the exported requirements file. The <code>--no-install-package</code> option allows exclusion of specific packages.</p>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue