mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-03 05:03:46 +00:00
Add uv run --no-sync (#7192)
## Summary When `--no-sync` is provided, we won't lock or sync, but we will run the command in the project environment. Closes https://github.com/astral-sh/uv/issues/7165.
This commit is contained in:
parent
4b7fed84eb
commit
3f011f3b7b
7 changed files with 142 additions and 56 deletions
|
|
@ -2434,6 +2434,13 @@ pub struct RunArgs {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub isolated: bool,
|
pub isolated: bool,
|
||||||
|
|
||||||
|
/// Avoid syncing the virtual environment.
|
||||||
|
///
|
||||||
|
/// Implies `--frozen`, as the project dependencies will be ignored (i.e., the lockfile will not
|
||||||
|
/// be updated, since the environment will not be synced regardless).
|
||||||
|
#[arg(long, conflicts_with = "frozen")]
|
||||||
|
pub no_sync: bool,
|
||||||
|
|
||||||
/// Assert that the `uv.lock` will remain unchanged.
|
/// Assert that the `uv.lock` will remain unchanged.
|
||||||
///
|
///
|
||||||
/// Requires that the lockfile is up-to-date. If the lockfile is missing or
|
/// Requires that the lockfile is up-to-date. If the lockfile is missing or
|
||||||
|
|
@ -2735,7 +2742,7 @@ pub struct AddArgs {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub extra: Option<Vec<ExtraName>>,
|
pub extra: Option<Vec<ExtraName>>,
|
||||||
|
|
||||||
/// Avoid syncing the virtual environment after re-locking the project.
|
/// Avoid syncing the virtual environment.
|
||||||
#[arg(long, conflicts_with = "frozen")]
|
#[arg(long, conflicts_with = "frozen")]
|
||||||
pub no_sync: bool,
|
pub no_sync: bool,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ pub(crate) async fn run(
|
||||||
show_resolution: bool,
|
show_resolution: bool,
|
||||||
locked: bool,
|
locked: bool,
|
||||||
frozen: bool,
|
frozen: bool,
|
||||||
|
no_sync: bool,
|
||||||
isolated: bool,
|
isolated: bool,
|
||||||
package: Option<PackageName>,
|
package: Option<PackageName>,
|
||||||
no_project: bool,
|
no_project: bool,
|
||||||
|
|
@ -258,6 +259,11 @@ pub(crate) async fn run(
|
||||||
"`--frozen` is a no-op for Python scripts with inline metadata, which always run in isolation"
|
"`--frozen` is a no-op for Python scripts with inline metadata, which always run in isolation"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if no_sync {
|
||||||
|
warn_user!(
|
||||||
|
"`--no-sync` is a no-op for Python scripts with inline metadata, which always run in isolation"
|
||||||
|
);
|
||||||
|
}
|
||||||
if isolated {
|
if isolated {
|
||||||
warn_user!(
|
warn_user!(
|
||||||
"`--isolated` is a no-op for Python scripts with inline metadata, which always run in isolation"
|
"`--isolated` is a no-op for Python scripts with inline metadata, which always run in isolation"
|
||||||
|
|
@ -318,6 +324,9 @@ pub(crate) async fn run(
|
||||||
if frozen {
|
if frozen {
|
||||||
warn_user!("`--frozen` has no effect when used alongside `--no-project`");
|
warn_user!("`--frozen` has no effect when used alongside `--no-project`");
|
||||||
}
|
}
|
||||||
|
if no_sync {
|
||||||
|
warn_user!("`--no-sync` has no effect when used alongside `--no-project`");
|
||||||
|
}
|
||||||
} else if project.is_none() {
|
} else if project.is_none() {
|
||||||
// If we can't find a project and the user provided a project-only setting, warn.
|
// If we can't find a project and the user provided a project-only setting, warn.
|
||||||
if !extras.is_empty() {
|
if !extras.is_empty() {
|
||||||
|
|
@ -329,8 +338,8 @@ pub(crate) async fn run(
|
||||||
if locked {
|
if locked {
|
||||||
warn_user!("`--locked` has no effect when used outside of a project");
|
warn_user!("`--locked` has no effect when used outside of a project");
|
||||||
}
|
}
|
||||||
if frozen {
|
if no_sync {
|
||||||
warn_user!("`--frozen` has no effect when used outside of a project");
|
warn_user!("`--no-sync` has no effect when used outside of a project");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -414,60 +423,64 @@ pub(crate) async fn run(
|
||||||
.await?
|
.await?
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = match project::lock::do_safe_lock(
|
if no_sync {
|
||||||
locked,
|
debug!("Skipping environment synchronization due to `--no-sync`");
|
||||||
frozen,
|
} else {
|
||||||
project.workspace(),
|
let result = match project::lock::do_safe_lock(
|
||||||
venv.interpreter(),
|
locked,
|
||||||
settings.as_ref().into(),
|
frozen,
|
||||||
if show_resolution {
|
project.workspace(),
|
||||||
Box::new(DefaultResolveLogger)
|
venv.interpreter(),
|
||||||
} else {
|
settings.as_ref().into(),
|
||||||
Box::new(SummaryResolveLogger)
|
if show_resolution {
|
||||||
},
|
Box::new(DefaultResolveLogger)
|
||||||
connectivity,
|
} else {
|
||||||
concurrency,
|
Box::new(SummaryResolveLogger)
|
||||||
native_tls,
|
},
|
||||||
cache,
|
connectivity,
|
||||||
printer,
|
concurrency,
|
||||||
)
|
native_tls,
|
||||||
.await
|
cache,
|
||||||
{
|
printer,
|
||||||
Ok(result) => result,
|
)
|
||||||
Err(ProjectError::Operation(operations::Error::Resolve(
|
.await
|
||||||
uv_resolver::ResolveError::NoSolution(err),
|
{
|
||||||
))) => {
|
Ok(result) => result,
|
||||||
let report = miette::Report::msg(format!("{err}")).context(err.header());
|
Err(ProjectError::Operation(operations::Error::Resolve(
|
||||||
eprint!("{report:?}");
|
uv_resolver::ResolveError::NoSolution(err),
|
||||||
return Ok(ExitStatus::Failure);
|
))) => {
|
||||||
}
|
let report = miette::Report::msg(format!("{err}")).context(err.header());
|
||||||
Err(err) => return Err(err.into()),
|
eprint!("{report:?}");
|
||||||
};
|
return Ok(ExitStatus::Failure);
|
||||||
|
}
|
||||||
|
Err(err) => return Err(err.into()),
|
||||||
|
};
|
||||||
|
|
||||||
let install_options = InstallOptions::default();
|
let install_options = InstallOptions::default();
|
||||||
|
|
||||||
project::sync::do_sync(
|
project::sync::do_sync(
|
||||||
InstallTarget::from(&project),
|
InstallTarget::from(&project),
|
||||||
&venv,
|
&venv,
|
||||||
result.lock(),
|
result.lock(),
|
||||||
&extras,
|
&extras,
|
||||||
dev,
|
dev,
|
||||||
install_options,
|
install_options,
|
||||||
Modifications::Sufficient,
|
Modifications::Sufficient,
|
||||||
settings.as_ref().into(),
|
settings.as_ref().into(),
|
||||||
&state,
|
&state,
|
||||||
if show_resolution {
|
if show_resolution {
|
||||||
Box::new(DefaultInstallLogger)
|
Box::new(DefaultInstallLogger)
|
||||||
} else {
|
} else {
|
||||||
Box::new(SummaryInstallLogger)
|
Box::new(SummaryInstallLogger)
|
||||||
},
|
},
|
||||||
connectivity,
|
connectivity,
|
||||||
concurrency,
|
concurrency,
|
||||||
native_tls,
|
native_tls,
|
||||||
cache,
|
cache,
|
||||||
printer,
|
printer,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
venv.into_interpreter()
|
venv.into_interpreter()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1123,6 +1123,7 @@ async fn run_project(
|
||||||
args.show_resolution || globals.verbose > 0,
|
args.show_resolution || globals.verbose > 0,
|
||||||
args.locked,
|
args.locked,
|
||||||
args.frozen,
|
args.frozen,
|
||||||
|
args.no_sync,
|
||||||
args.isolated,
|
args.isolated,
|
||||||
args.package,
|
args.package,
|
||||||
args.no_project,
|
args.no_project,
|
||||||
|
|
|
||||||
|
|
@ -217,6 +217,7 @@ pub(crate) struct RunSettings {
|
||||||
pub(crate) show_resolution: bool,
|
pub(crate) show_resolution: bool,
|
||||||
pub(crate) package: Option<PackageName>,
|
pub(crate) package: Option<PackageName>,
|
||||||
pub(crate) no_project: bool,
|
pub(crate) no_project: bool,
|
||||||
|
pub(crate) no_sync: bool,
|
||||||
pub(crate) python: Option<String>,
|
pub(crate) python: Option<String>,
|
||||||
pub(crate) refresh: Refresh,
|
pub(crate) refresh: Refresh,
|
||||||
pub(crate) settings: ResolverInstallerSettings,
|
pub(crate) settings: ResolverInstallerSettings,
|
||||||
|
|
@ -237,6 +238,7 @@ impl RunSettings {
|
||||||
with_editable,
|
with_editable,
|
||||||
with_requirements,
|
with_requirements,
|
||||||
isolated,
|
isolated,
|
||||||
|
no_sync,
|
||||||
locked,
|
locked,
|
||||||
frozen,
|
frozen,
|
||||||
installer,
|
installer,
|
||||||
|
|
@ -266,6 +268,7 @@ impl RunSettings {
|
||||||
show_resolution,
|
show_resolution,
|
||||||
package,
|
package,
|
||||||
no_project,
|
no_project,
|
||||||
|
no_sync,
|
||||||
python,
|
python,
|
||||||
refresh: Refresh::from(refresh),
|
refresh: Refresh::from(refresh),
|
||||||
settings: ResolverInstallerSettings::combine(
|
settings: ResolverInstallerSettings::combine(
|
||||||
|
|
|
||||||
|
|
@ -976,6 +976,62 @@ fn run_frozen() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn run_no_sync() -> 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"]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Running with `--no-sync` should succeed error, even if the lockfile isn't present.
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--no-sync").arg("--").arg("python").arg("--version"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
Python 3.12.[X]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
|
||||||
|
context.lock().assert().success();
|
||||||
|
|
||||||
|
// Running with `--no-sync` should not install any requirements.
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--no-sync").arg("--").arg("python").arg("--version"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
Python 3.12.[X]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
|
||||||
|
context.sync().assert().success();
|
||||||
|
|
||||||
|
// But it should have access to the installed packages.
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--no-sync").arg("--").arg("python").arg("-c").arg("import anyio; print(anyio.__name__)"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
anyio
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn run_empty_requirements_txt() -> Result<()> {
|
fn run_empty_requirements_txt() -> Result<()> {
|
||||||
let context = TestContext::new("3.12");
|
let context = TestContext::new("3.12");
|
||||||
|
|
|
||||||
|
|
@ -358,6 +358,8 @@ and not usable by other tools.
|
||||||
|
|
||||||
To avoid updating the lockfile during `uv sync` and `uv run` invocations, use the `--frozen` flag.
|
To avoid updating the lockfile during `uv sync` and `uv run` invocations, use the `--frozen` flag.
|
||||||
|
|
||||||
|
To avoid updating the environment during `uv run` invocations, use the `--no-sync` flag.
|
||||||
|
|
||||||
To assert the lockfile matches the project metadata, use the `--locked` flag. If the lockfile is not
|
To assert the lockfile matches the project metadata, use the `--locked` flag. If the lockfile is not
|
||||||
up-to-date, an error will be raised instead of updating the lockfile.
|
up-to-date, an error will be raised instead of updating the lockfile.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -265,6 +265,10 @@ uv run [OPTIONS] <COMMAND>
|
||||||
|
|
||||||
</dd><dt><code>--no-sources</code></dt><dd><p>Ignore the <code>tool.uv.sources</code> table when resolving dependencies. Used to lock against the standards-compliant, publishable package metadata, as opposed to using any local or Git sources</p>
|
</dd><dt><code>--no-sources</code></dt><dd><p>Ignore the <code>tool.uv.sources</code> table when resolving dependencies. Used to lock against the standards-compliant, publishable package metadata, as opposed to using any local or Git sources</p>
|
||||||
|
|
||||||
|
</dd><dt><code>--no-sync</code></dt><dd><p>Avoid syncing the virtual environment.</p>
|
||||||
|
|
||||||
|
<p>Implies <code>--frozen</code>, as the project dependencies will be ignored (i.e., the lockfile will not be updated, since the environment will not be synced regardless).</p>
|
||||||
|
|
||||||
</dd><dt><code>--offline</code></dt><dd><p>Disable network access.</p>
|
</dd><dt><code>--offline</code></dt><dd><p>Disable network access.</p>
|
||||||
|
|
||||||
<p>When disabled, uv will only use locally cached data and locally available files.</p>
|
<p>When disabled, uv will only use locally cached data and locally available files.</p>
|
||||||
|
|
@ -721,7 +725,7 @@ uv add [OPTIONS] <PACKAGES|--requirements <REQUIREMENTS>>
|
||||||
|
|
||||||
</dd><dt><code>--no-sources</code></dt><dd><p>Ignore the <code>tool.uv.sources</code> table when resolving dependencies. Used to lock against the standards-compliant, publishable package metadata, as opposed to using any local or Git sources</p>
|
</dd><dt><code>--no-sources</code></dt><dd><p>Ignore the <code>tool.uv.sources</code> table when resolving dependencies. Used to lock against the standards-compliant, publishable package metadata, as opposed to using any local or Git sources</p>
|
||||||
|
|
||||||
</dd><dt><code>--no-sync</code></dt><dd><p>Avoid syncing the virtual environment after re-locking the project</p>
|
</dd><dt><code>--no-sync</code></dt><dd><p>Avoid syncing the virtual environment</p>
|
||||||
|
|
||||||
</dd><dt><code>--offline</code></dt><dd><p>Disable network access.</p>
|
</dd><dt><code>--offline</code></dt><dd><p>Disable network access.</p>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue