Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add uv run --no-sync #7192

Merged
merged 1 commit into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2434,6 +2434,13 @@ pub struct RunArgs {
#[arg(long)]
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.
///
/// Requires that the lockfile is up-to-date. If the lockfile is missing or
Expand Down Expand Up @@ -2735,7 +2742,7 @@ pub struct AddArgs {
#[arg(long)]
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")]
pub no_sync: bool,

Expand Down
123 changes: 68 additions & 55 deletions crates/uv/src/commands/project/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub(crate) async fn run(
show_resolution: bool,
locked: bool,
frozen: bool,
no_sync: bool,
isolated: bool,
package: Option<PackageName>,
no_project: bool,
Expand Down Expand Up @@ -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"
);
}
if no_sync {
warn_user!(
"`--no-sync` is a no-op for Python scripts with inline metadata, which always run in isolation"
);
}
if isolated {
warn_user!(
"`--isolated` is a no-op for Python scripts with inline metadata, which always run in isolation"
Expand Down Expand Up @@ -318,6 +324,9 @@ pub(crate) async fn run(
if frozen {
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() {
// If we can't find a project and the user provided a project-only setting, warn.
if !extras.is_empty() {
Expand All @@ -329,8 +338,8 @@ pub(crate) async fn run(
if locked {
warn_user!("`--locked` has no effect when used outside of a project");
}
if frozen {
warn_user!("`--frozen` has no effect when used outside of a project");
if no_sync {
warn_user!("`--no-sync` has no effect when used outside of a project");
}
}

Expand Down Expand Up @@ -414,60 +423,64 @@ pub(crate) async fn run(
.await?
};

let result = match project::lock::do_safe_lock(
locked,
frozen,
project.workspace(),
venv.interpreter(),
settings.as_ref().into(),
if show_resolution {
Box::new(DefaultResolveLogger)
} else {
Box::new(SummaryResolveLogger)
},
connectivity,
concurrency,
native_tls,
cache,
printer,
)
.await
{
Ok(result) => result,
Err(ProjectError::Operation(operations::Error::Resolve(
uv_resolver::ResolveError::NoSolution(err),
))) => {
let report = miette::Report::msg(format!("{err}")).context(err.header());
eprint!("{report:?}");
return Ok(ExitStatus::Failure);
}
Err(err) => return Err(err.into()),
};
if no_sync {
debug!("Skipping environment synchronization due to `--no-sync`");
} else {
let result = match project::lock::do_safe_lock(
locked,
frozen,
project.workspace(),
venv.interpreter(),
settings.as_ref().into(),
if show_resolution {
Box::new(DefaultResolveLogger)
} else {
Box::new(SummaryResolveLogger)
},
connectivity,
concurrency,
native_tls,
cache,
printer,
)
.await
{
Ok(result) => result,
Err(ProjectError::Operation(operations::Error::Resolve(
uv_resolver::ResolveError::NoSolution(err),
))) => {
let report = miette::Report::msg(format!("{err}")).context(err.header());
eprint!("{report:?}");
return Ok(ExitStatus::Failure);
}
Err(err) => return Err(err.into()),
};

let install_options = InstallOptions::default();

project::sync::do_sync(
InstallTarget::from(&project),
&venv,
result.lock(),
&extras,
dev,
install_options,
Modifications::Sufficient,
settings.as_ref().into(),
&state,
if show_resolution {
Box::new(DefaultInstallLogger)
} else {
Box::new(SummaryInstallLogger)
},
connectivity,
concurrency,
native_tls,
cache,
printer,
)
.await?;
let install_options = InstallOptions::default();

project::sync::do_sync(
InstallTarget::from(&project),
&venv,
result.lock(),
&extras,
dev,
install_options,
Modifications::Sufficient,
settings.as_ref().into(),
&state,
if show_resolution {
Box::new(DefaultInstallLogger)
} else {
Box::new(SummaryInstallLogger)
},
connectivity,
concurrency,
native_tls,
cache,
printer,
)
.await?;
}

venv.into_interpreter()
} else {
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,7 @@ async fn run_project(
args.show_resolution || globals.verbose > 0,
args.locked,
args.frozen,
args.no_sync,
args.isolated,
args.package,
args.no_project,
Expand Down
3 changes: 3 additions & 0 deletions crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ pub(crate) struct RunSettings {
pub(crate) show_resolution: bool,
pub(crate) package: Option<PackageName>,
pub(crate) no_project: bool,
pub(crate) no_sync: bool,
pub(crate) python: Option<String>,
pub(crate) refresh: Refresh,
pub(crate) settings: ResolverInstallerSettings,
Expand All @@ -237,6 +238,7 @@ impl RunSettings {
with_editable,
with_requirements,
isolated,
no_sync,
locked,
frozen,
installer,
Expand Down Expand Up @@ -266,6 +268,7 @@ impl RunSettings {
show_resolution,
package,
no_project,
no_sync,
python,
refresh: Refresh::from(refresh),
settings: ResolverInstallerSettings::combine(
Expand Down
56 changes: 56 additions & 0 deletions crates/uv/tests/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,62 @@ fn run_frozen() -> Result<()> {
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]
fn run_empty_requirements_txt() -> Result<()> {
let context = TestContext::new("3.12");
Expand Down
2 changes: 2 additions & 0 deletions docs/concepts/projects.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,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 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
up-to-date, an error will be raised instead of updating the lockfile.

Expand Down
6 changes: 5 additions & 1 deletion docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-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>

<p>When disabled, uv will only use locally cached data and locally available files.</p>
Expand Down Expand Up @@ -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-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>

Expand Down
Loading