From f1b3d2e1e122febc2813d5f8250e30d6a79c8760 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 30 Jul 2024 13:40:35 -0400 Subject: [PATCH] Add `--no-workspace` and `--no-project` in lieu of `--isolated` (#5465) ## Summary Right now, `--isolated` is read from `uv run` and `uv init` to avoid discovering the current workspace (or project). This PR moves that behavior to a dedicated `--no-workspace` flag for `uv init`, and `--no-project` for `uv run`. They could use the same flag, but `--no-project` feels confusing for `uv init`, and `--no-workspace` seems confusing for `uv run` (especially so once you read the documentation, where we refer to the thing you're omitting as the project). Closes https://github.com/astral-sh/uv/issues/5429. --- crates/uv-cli/src/lib.rs | 12 ++++++- crates/uv/src/commands/project/init.rs | 14 ++++---- crates/uv/src/commands/project/run.rs | 6 ++-- crates/uv/src/lib.rs | 4 +-- crates/uv/src/settings.rs | 6 ++++ crates/uv/tests/init.rs | 47 +++++++++++++++++++++++++- 6 files changed, 75 insertions(+), 14 deletions(-) diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 7ae4a8a27235..13af9ebe25d7 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -1847,6 +1847,11 @@ pub struct InitArgs { #[arg(long)] pub no_readme: bool, + /// Avoid discovering the workspace in the current directory or any parent directory. Instead, + /// create a standalone project. + #[arg(long, alias = "no_project")] + pub no_workspace: bool, + /// The Python interpreter to use to determine the minimum supported Python version. /// /// By default, uv uses the virtual environment in the current working directory or any parent @@ -1920,9 +1925,14 @@ pub struct RunArgs { pub refresh: RefreshArgs, /// Run the command in a specific package in the workspace. - #[arg(long, conflicts_with = "isolated")] + #[arg(long)] pub package: Option, + /// Avoid discovering the project or workspace in the current directory or any parent directory. + /// Instead, run in an isolated, ephemeral environment populated by the `--with` requirements. + #[arg(long, alias = "no_workspace", conflicts_with = "package")] + pub no_project: bool, + /// The Python interpreter to use to build the run environment. /// /// By default, uv uses the virtual environment in the current working directory or any parent diff --git a/crates/uv/src/commands/project/init.rs b/crates/uv/src/commands/project/init.rs index f6768dfa3b9d..c5d896b384b1 100644 --- a/crates/uv/src/commands/project/init.rs +++ b/crates/uv/src/commands/project/init.rs @@ -32,7 +32,7 @@ pub(crate) async fn init( r#virtual: bool, no_readme: bool, python: Option, - isolated: bool, + no_workspace: bool, preview: PreviewMode, python_preference: PythonPreference, python_fetch: PythonFetch, @@ -77,14 +77,14 @@ pub(crate) async fn init( }; if r#virtual { - init_virtual_workspace(&path, isolated)?; + init_virtual_workspace(&path, no_workspace)?; } else { init_project( &path, &name, no_readme, python, - isolated, + no_workspace, python_preference, python_fetch, connectivity, @@ -134,9 +134,9 @@ pub(crate) async fn init( } /// Initialize a virtual workspace at the given path. -fn init_virtual_workspace(path: &Path, isolated: bool) -> Result<()> { +fn init_virtual_workspace(path: &Path, no_workspace: bool) -> Result<()> { // Ensure that we aren't creating a nested workspace. - if !isolated { + if !no_workspace { check_nested_workspaces(path, &DiscoveryOptions::default()); } @@ -158,7 +158,7 @@ async fn init_project( name: &PackageName, no_readme: bool, python: Option, - isolated: bool, + no_workspace: bool, python_preference: PythonPreference, python_fetch: PythonFetch, connectivity: Connectivity, @@ -167,7 +167,7 @@ async fn init_project( printer: Printer, ) -> Result<()> { // Discover the current workspace, if it exists. - let workspace = if isolated { + let workspace = if no_workspace { None } else { // Attempt to find a workspace root. diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs index b20dbd22a087..c6b1ead0530c 100644 --- a/crates/uv/src/commands/project/run.rs +++ b/crates/uv/src/commands/project/run.rs @@ -45,7 +45,7 @@ pub(crate) async fn run( dev: bool, python: Option, settings: ResolverInstallerSettings, - isolated: bool, + no_project: bool, preview: PreviewMode, python_preference: PythonPreference, python_fetch: PythonFetch, @@ -159,8 +159,8 @@ pub(crate) async fn run( // Discover and sync the base environment. let base_interpreter = if let Some(script_interpreter) = script_interpreter { Some(script_interpreter) - } else if isolated { - // package is `None`, isolated and package are marked as conflicting in clap. + } else if no_project { + // package is `None` (`--no-project` and `--package` are marked as conflicting in Clap). None } else { let project = if let Some(package) = package { diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 3139d978f1ca..e1eff7477b8b 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -879,7 +879,7 @@ async fn run_project( args.r#virtual, args.no_readme, args.python, - globals.isolated, + args.no_workspace || globals.isolated, globals.preview, globals.python_preference, globals.python_fetch, @@ -921,7 +921,7 @@ async fn run_project( args.dev, args.python, args.settings, - globals.isolated, + args.no_project || globals.isolated, globals.preview, globals.python_preference, globals.python_fetch, diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index 880f3d4f7c2a..5c7e5b59e658 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -155,6 +155,7 @@ pub(crate) struct InitSettings { pub(crate) name: Option, pub(crate) r#virtual: bool, pub(crate) no_readme: bool, + pub(crate) no_workspace: bool, pub(crate) python: Option, } @@ -167,6 +168,7 @@ impl InitSettings { name, r#virtual, no_readme, + no_workspace, python, } = args; @@ -175,6 +177,7 @@ impl InitSettings { name, r#virtual, no_readme, + no_workspace, python, } } @@ -192,6 +195,7 @@ pub(crate) struct RunSettings { pub(crate) with: Vec, pub(crate) with_requirements: Vec, pub(crate) package: Option, + pub(crate) no_project: bool, pub(crate) python: Option, pub(crate) refresh: Refresh, pub(crate) settings: ResolverInstallerSettings, @@ -216,6 +220,7 @@ impl RunSettings { build, refresh, package, + no_project, python, } = args; @@ -234,6 +239,7 @@ impl RunSettings { .filter_map(Maybe::into_option) .collect(), package, + no_project, python, refresh: Refresh::from(refresh), settings: ResolverInstallerSettings::combine( diff --git a/crates/uv/tests/init.rs b/crates/uv/tests/init.rs index bbf693e6407e..4ff99cdfcc34 100644 --- a/crates/uv/tests/init.rs +++ b/crates/uv/tests/init.rs @@ -590,7 +590,7 @@ fn init_invalid_names() -> Result<()> { } #[test] -fn init_workspace_isolated() -> Result<()> { +fn init_isolated() -> Result<()> { let context = TestContext::new("3.12"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); @@ -634,6 +634,51 @@ fn init_workspace_isolated() -> Result<()> { Ok(()) } +#[test] +fn init_no_workspace() -> Result<()> { + let context = TestContext::new("3.12"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str(indoc! { + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + "#, + })?; + + let child = context.temp_dir.join("foo"); + fs_err::create_dir(&child)?; + + uv_snapshot!(context.filters(), context.init().current_dir(&child).arg("--no-workspace"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + warning: `uv init` is experimental and may change without warning + Initialized project `foo` + "###); + + let workspace = fs_err::read_to_string(context.temp_dir.join("pyproject.toml"))?; + + insta::with_settings!({ + filters => context.filters(), + }, { + assert_snapshot!( + workspace, @r###" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + "### + ); + }); + + Ok(()) +} + #[test] fn init_project_inside_project() -> Result<()> { let context = TestContext::new("3.12");