Require uv venv --clear before clearing an existing directory

This commit is contained in:
John Mumm 2025-06-26 11:39:39 +02:00
parent 3774a656d7
commit dc2473af28
No known key found for this signature in database
GPG key ID: 73D2271AFDC26EA8
24 changed files with 146 additions and 71 deletions

View file

@ -981,7 +981,7 @@ jobs:
- name: "Create a virtual environment (uv)"
run: |
./uv venv -p 3.13t --managed-python
./uv venv -c -p 3.13t --managed-python
- name: "Check version (uv)"
run: |
@ -1697,14 +1697,14 @@ jobs:
./uv run --no-project python -c "from built_by_uv import greet; print(greet())"
# Test both `build_wheel` and `build_sdist` through uv
./uv venv -v
./uv venv -c -v
./uv build -v --force-pep517 scripts/packages/built-by-uv --find-links crates/uv-build/dist --offline
./uv pip install -v scripts/packages/built-by-uv/dist/*.tar.gz --find-links crates/uv-build/dist --offline --no-deps
./uv run --no-project python -c "from built_by_uv import greet; print(greet())"
# Test both `build_wheel` and `build_sdist` through the official `build`
rm -rf scripts/packages/built-by-uv/dist/
./uv venv -v
./uv venv -c -v
./uv pip install build
# Add the uv binary to PATH for `build` to find
PATH="$(pwd):$PATH" UV_OFFLINE=1 UV_FIND_LINKS=crates/uv-build/dist ./uv run --no-project python -m build -v --installer uv scripts/packages/built-by-uv

3
Cargo.lock generated
View file

@ -6075,13 +6075,16 @@ version = "0.7.17"
name = "uv-virtualenv"
version = "0.0.4"
dependencies = [
"console",
"fs-err 3.1.1",
"itertools 0.14.0",
"owo-colors",
"pathdiff",
"self-replace",
"thiserror 2.0.12",
"tracing",
"uv-configuration",
"uv-console",
"uv-fs",
"uv-pypi-types",
"uv-python",

View file

@ -334,6 +334,7 @@ impl SourceBuild {
uv_virtualenv::Prompt::None,
false,
false,
true,
false,
false,
false,

View file

@ -266,9 +266,6 @@ enum Resolver {
/// These represent a subset of the `virtualenv` interface that uv supports by default.
#[derive(Args)]
pub struct VenvCompatArgs {
#[clap(long, hide = true)]
clear: bool,
#[clap(long, hide = true)]
no_seed: bool,
@ -289,12 +286,6 @@ impl CompatArgs for VenvCompatArgs {
/// behavior. If an argument is passed that does _not_ match uv's behavior, this method will
/// return an error.
fn validate(&self) -> Result<()> {
if self.clear {
warn_user!(
"virtualenv's `--clear` has no effect (uv always clears the virtual environment)"
);
}
if self.no_seed {
warn_user!(
"virtualenv's `--no-seed` has no effect (uv omits seed packages by default)"

View file

@ -2562,16 +2562,23 @@ pub struct VenvArgs {
#[arg(long, value_parser = clap::builder::BoolishValueParser::new(), env = EnvVars::UV_VENV_SEED)]
pub seed: bool,
/// Remove any existing files or directories at the target path.
///
/// By default, `uv venv` will exit with an error if the given path is non-empty. The
/// `--clear` option will instead clear a non-empty path before creating a new virtual
/// environment.
#[clap(long, short, conflicts_with = "allow_existing", value_parser = clap::builder::BoolishValueParser::new(), env = EnvVars::UV_VENV_CLEAR)]
pub clear: bool,
/// Preserve any existing files or directories at the target path.
///
/// By default, `uv venv` will remove an existing virtual environment at the given path, and
/// exit with an error if the path is non-empty but _not_ a virtual environment. The
/// By default, `uv venv` will exit with an error if the given path is non-empty. The
/// `--allow-existing` option will instead write to the given path, regardless of its contents,
/// and without clearing it beforehand.
///
/// WARNING: This option can lead to unexpected behavior if the existing virtual environment and
/// the newly-created virtual environment are linked to different Python interpreters.
#[clap(long)]
#[clap(long, conflicts_with = "clear")]
pub allow_existing: bool,
/// The path to the virtual environment to create.

View file

@ -292,6 +292,13 @@ impl EnvVars {
/// Distributions can be read from a local directory by using the `file://` URL scheme.
pub const UV_PYPY_INSTALL_MIRROR: &'static str = "UV_PYPY_INSTALL_MIRROR";
/// Remove any existing files or directories at the target path.
///
/// By default, `uv venv` will exit with an error if the given path is non-empty. The
/// `--clear` option will instead clear a non-empty path before creating a new virtual
/// environment.
pub const UV_VENV_CLEAR: &'static str = "UV_VENV_CLEAR";
/// Install seed packages (one or more of: `pip`, `setuptools`, and `wheel`) into the virtual environment
/// created by `uv venv`.
///

View file

@ -286,6 +286,7 @@ impl InstalledTools {
uv_virtualenv::Prompt::None,
false,
false,
true,
false,
false,
false,

View file

@ -21,14 +21,17 @@ workspace = true
[dependencies]
uv-configuration = { workspace = true }
uv-console = { workspace = true }
uv-fs = { workspace = true }
uv-pypi-types = { workspace = true }
uv-python = { workspace = true }
uv-shell = { workspace = true }
uv-version = { workspace = true }
console = { workspace = true }
fs-err = { workspace = true }
itertools = { workspace = true }
owo-colors = { workspace = true }
pathdiff = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }

View file

@ -51,6 +51,7 @@ pub fn create_venv(
prompt: Prompt,
system_site_packages: bool,
allow_existing: bool,
clear: bool,
relocatable: bool,
seed: bool,
upgradeable: bool,
@ -63,6 +64,7 @@ pub fn create_venv(
prompt,
system_site_packages,
allow_existing,
clear,
relocatable,
seed,
upgradeable,

View file

@ -5,9 +5,11 @@ use std::io;
use std::io::{BufWriter, Write};
use std::path::Path;
use console::Term;
use fs_err as fs;
use fs_err::File;
use itertools::Itertools;
use owo_colors::OwoColorize;
use tracing::debug;
use uv_configuration::PreviewMode;
@ -53,6 +55,7 @@ pub(crate) fn create(
prompt: Prompt,
system_site_packages: bool,
allow_existing: bool,
clear: bool,
relocatable: bool,
seed: bool,
upgradeable: bool,
@ -83,10 +86,17 @@ pub(crate) fn create(
format!("File exists at `{}`", location.user_display()),
)));
} else if metadata.is_dir() {
let confirmation_required = !clear && !allow_existing;
let confirmed_clear = confirmation_required && confirm_clear(location)?;
if allow_existing {
debug!("Allowing existing directory");
} else if uv_fs::is_virtualenv_base(location) {
debug!("Removing existing directory");
debug!("Allowing existing directory due to `--allow-existing`");
} else if clear || confirmed_clear {
if clear {
debug!("Removing existing directory due to `--clear`");
} else {
debug!("Removing existing directory");
}
// On Windows, if the current executable is in the directory, guard against
// self-deletion.
@ -110,8 +120,12 @@ pub(crate) fn create(
return Err(Error::Io(io::Error::new(
io::ErrorKind::AlreadyExists,
format!(
"The directory `{}` exists, but it's not a virtual environment",
location.user_display()
"The directory `{}` exists. \n\n{}{} Use `{}` to remove the directory first or `{}` to write to the directory without clearing",
location.user_display(),
"hint".bold().cyan(),
":".bold(),
"--clear".green(),
"--allow-existing".green(),
),
)));
}
@ -464,6 +478,19 @@ pub(crate) fn create(
})
}
fn confirm_clear(location: &Path) -> Result<bool, io::Error> {
let term = Term::stderr();
if term.is_term() {
let prompt = format!(
"The directory `{}` exists. Did you mean to clear its contents (`--clear`)?",
location.user_display(),
);
uv_console::confirm(&prompt, &term, true)
} else {
Ok(false)
}
}
#[derive(Debug, Copy, Clone)]
enum WindowsExecutable {
/// The `python.exe` executable (or `venvlauncher.exe` launcher shim).

View file

@ -98,6 +98,7 @@ impl CachedEnvironment {
false,
false,
true,
true,
false,
false,
preview,

View file

@ -1309,6 +1309,7 @@ impl ProjectEnvironment {
prompt,
false,
false,
true,
false,
false,
upgradeable,
@ -1348,6 +1349,7 @@ impl ProjectEnvironment {
prompt,
false,
false,
true,
false,
false,
upgradeable,
@ -1486,6 +1488,7 @@ impl ScriptEnvironment {
prompt,
false,
false,
true,
false,
false,
upgradeable,
@ -1522,6 +1525,7 @@ impl ScriptEnvironment {
prompt,
false,
false,
true,
false,
false,
upgradeable,

View file

@ -453,6 +453,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
uv_virtualenv::Prompt::None,
false,
false,
true,
false,
false,
false,
@ -657,6 +658,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
uv_virtualenv::Prompt::None,
false,
false,
true,
false,
false,
false,
@ -886,6 +888,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
uv_virtualenv::Prompt::None,
false,
false,
true,
false,
false,
false,

View file

@ -61,6 +61,7 @@ pub(crate) async fn venv(
system_site_packages: bool,
seed: bool,
allow_existing: bool,
clear: bool,
exclude_newer: Option<ExcludeNewer>,
concurrency: Concurrency,
no_config: bool,
@ -87,6 +88,7 @@ pub(crate) async fn venv(
python_preference,
python_downloads,
allow_existing,
clear,
exclude_newer,
concurrency,
no_config,
@ -144,6 +146,7 @@ async fn venv_impl(
python_preference: PythonPreference,
python_downloads: PythonDownloads,
allow_existing: bool,
clear: bool,
exclude_newer: Option<ExcludeNewer>,
concurrency: Concurrency,
no_config: bool,
@ -289,6 +292,7 @@ async fn venv_impl(
prompt,
system_site_packages,
allow_existing,
clear,
relocatable,
seed,
upgradeable,

View file

@ -1043,6 +1043,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
args.system_site_packages,
args.seed,
args.allow_existing,
args.clear,
args.settings.exclude_newer,
globals.concurrency,
cli.top_level.no_config,

View file

@ -2561,6 +2561,7 @@ impl BuildSettings {
pub(crate) struct VenvSettings {
pub(crate) seed: bool,
pub(crate) allow_existing: bool,
pub(crate) clear: bool,
pub(crate) path: Option<PathBuf>,
pub(crate) prompt: Option<String>,
pub(crate) system_site_packages: bool,
@ -2579,6 +2580,7 @@ impl VenvSettings {
no_system,
seed,
allow_existing,
clear,
path,
prompt,
system_site_packages,
@ -2596,6 +2598,7 @@ impl VenvSettings {
Self {
seed,
allow_existing,
clear,
path,
prompt,
system_site_packages,

View file

@ -227,7 +227,7 @@ fn prune_unzipped() -> Result<()> {
Removed [N] files ([SIZE])
"###);
context.venv().assert().success();
context.venv().arg("--clear").assert().success();
// Reinstalling the source distribution should not require re-downloading the source
// distribution.

View file

@ -1390,6 +1390,7 @@ pub fn create_venv_from_executable<P: AsRef<Path>>(path: P, cache_dir: &ChildPat
assert_cmd::Command::new(get_bin())
.arg("venv")
.arg(path.as_ref().as_os_str())
.arg("--clear")
.arg("--cache-dir")
.arg(cache_dir.path())
.arg("--python")

View file

@ -2776,7 +2776,7 @@ fn install_no_binary_cache() {
);
// Re-create the virtual environment.
context.venv().assert().success();
context.venv().arg("--clear").assert().success();
// Re-install. The distribution should be installed from the cache.
uv_snapshot!(
@ -2794,7 +2794,7 @@ fn install_no_binary_cache() {
);
// Re-create the virtual environment.
context.venv().assert().success();
context.venv().arg("--clear").assert().success();
// Install with `--no-binary`. The distribution should be built from source, despite a binary
// distribution being available in the cache.
@ -3005,7 +3005,7 @@ fn cache_priority() {
);
// Re-create the virtual environment.
context.venv().assert().success();
context.venv().arg("--clear").assert().success();
// Install `idna` without a version specifier.
uv_snapshot!(
@ -8175,6 +8175,7 @@ fn install_relocatable() -> Result<()> {
context
.venv()
.arg(context.venv.as_os_str())
.arg("--clear")
.arg("--python")
.arg("3.12")
.arg("--relocatable")

View file

@ -5625,7 +5625,7 @@ fn sync_seed() -> Result<()> {
);
// Re-create the environment with seed packages.
uv_snapshot!(context.filters(), context.venv()
uv_snapshot!(context.filters(), context.venv().arg("--clear")
.arg("--seed"), @r"
success: true
exit_code: 0

View file

@ -9595,6 +9595,7 @@ fn sync_when_virtual_environment_incompatible_with_interpreter() -> Result<()> {
context
.venv()
.arg(context.venv.as_os_str())
.arg("--clear")
.arg("--python")
.arg("3.12")
.assert()

View file

@ -30,10 +30,34 @@ fn create_venv() {
context.venv.assert(predicates::path::is_dir());
// Create a virtual environment at the same location, which should replace it.
// Attempt to create a virtual environment at the same location,
// which should fail.
uv_snapshot!(context.filters(), context.venv()
.arg(context.venv.as_os_str())
.arg("--python")
.arg("3.12"), @r"
success: false
exit_code: 1
----- stdout -----
----- stderr -----
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
Creating virtual environment at: .venv
uv::venv::creation
× Failed to create virtualenv
The directory `.venv` exists.
hint: Use `--clear` to remove the directory first or `--allow-existing` to write to the directory without clearing
"
);
// Create a virtual environment at the same location using `--clear`,
// which should replace it.
uv_snapshot!(context.filters(), context.venv()
.arg(context.venv.as_os_str())
.arg("--clear")
.arg("--python")
.arg("3.12"), @r###"
success: true
exit_code: 0
@ -162,7 +186,7 @@ fn create_venv_project_environment() -> Result<()> {
.assert(predicates::path::is_dir());
// Or, of they opt-out with `--no-workspace` or `--no-project`
uv_snapshot!(context.filters(), context.venv().arg("--no-workspace"), @r###"
uv_snapshot!(context.filters(), context.venv().arg("--clear").arg("--no-workspace"), @r###"
success: true
exit_code: 0
----- stdout -----
@ -174,7 +198,7 @@ fn create_venv_project_environment() -> Result<()> {
"###
);
uv_snapshot!(context.filters(), context.venv().arg("--no-project"), @r###"
uv_snapshot!(context.filters(), context.venv().arg("--clear").arg("--no-project"), @r###"
success: true
exit_code: 0
----- stdout -----
@ -252,7 +276,7 @@ fn create_venv_reads_request_from_python_version_file() {
.write_str("3.12")
.unwrap();
uv_snapshot!(context.filters(), context.venv(), @r###"
uv_snapshot!(context.filters(), context.venv().arg("--clear"), @r###"
success: true
exit_code: 0
----- stdout -----
@ -291,7 +315,7 @@ fn create_venv_reads_request_from_python_versions_file() {
.write_str("3.12\n3.11")
.unwrap();
uv_snapshot!(context.filters(), context.venv(), @r###"
uv_snapshot!(context.filters(), context.venv().arg("--clear"), @r###"
success: true
exit_code: 0
----- stdout -----
@ -334,7 +358,7 @@ fn create_venv_respects_pyproject_requires_python() -> Result<()> {
"#
})?;
uv_snapshot!(context.filters(), context.venv(), @r###"
uv_snapshot!(context.filters(), context.venv().arg("--clear"), @r###"
success: true
exit_code: 0
----- stdout -----
@ -357,7 +381,7 @@ fn create_venv_respects_pyproject_requires_python() -> Result<()> {
"#
})?;
uv_snapshot!(context.filters(), context.venv(), @r###"
uv_snapshot!(context.filters(), context.venv().arg("--clear"), @r###"
success: true
exit_code: 0
----- stdout -----
@ -380,7 +404,7 @@ fn create_venv_respects_pyproject_requires_python() -> Result<()> {
"#
})?;
uv_snapshot!(context.filters(), context.venv(), @r###"
uv_snapshot!(context.filters(), context.venv().arg("--clear"), @r###"
success: true
exit_code: 0
----- stdout -----
@ -414,7 +438,7 @@ fn create_venv_respects_pyproject_requires_python() -> Result<()> {
"#
})?;
uv_snapshot!(context.filters(), context.venv(), @r###"
uv_snapshot!(context.filters(), context.venv().arg("--clear"), @r###"
success: true
exit_code: 0
----- stdout -----
@ -437,7 +461,7 @@ fn create_venv_respects_pyproject_requires_python() -> Result<()> {
"#
})?;
uv_snapshot!(context.filters(), context.venv(), @r###"
uv_snapshot!(context.filters(), context.venv().arg("--clear"), @r###"
success: true
exit_code: 0
----- stdout -----
@ -460,7 +484,7 @@ fn create_venv_respects_pyproject_requires_python() -> Result<()> {
"#
})?;
uv_snapshot!(context.filters(), context.venv(), @r###"
uv_snapshot!(context.filters(), context.venv().arg("--clear"), @r###"
success: true
exit_code: 0
----- stdout -----
@ -475,7 +499,7 @@ fn create_venv_respects_pyproject_requires_python() -> Result<()> {
context.venv.assert(predicates::path::is_dir());
// We warn if we receive an incompatible version
uv_snapshot!(context.filters(), context.venv().arg("--python").arg("3.11"), @r"
uv_snapshot!(context.filters(), context.venv().arg("--clear").arg("--python").arg("3.11"), @r"
success: true
exit_code: 0
----- stdout -----
@ -527,7 +551,7 @@ fn create_venv_respects_group_requires_python() -> Result<()> {
"#
})?;
uv_snapshot!(context.filters(), context.venv(), @r"
uv_snapshot!(context.filters(), context.venv().arg("--clear"), @r"
success: true
exit_code: 0
----- stdout -----
@ -560,7 +584,7 @@ fn create_venv_respects_group_requires_python() -> Result<()> {
"#
})?;
uv_snapshot!(context.filters(), context.venv(), @r"
uv_snapshot!(context.filters(), context.venv().arg("--clear"), @r"
success: true
exit_code: 0
----- stdout -----
@ -593,7 +617,7 @@ fn create_venv_respects_group_requires_python() -> Result<()> {
"#
})?;
uv_snapshot!(context.filters(), context.venv(), @r"
uv_snapshot!(context.filters(), context.venv().arg("--clear"), @r"
success: true
exit_code: 0
----- stdout -----
@ -621,7 +645,7 @@ fn create_venv_respects_group_requires_python() -> Result<()> {
"#
})?;
uv_snapshot!(context.filters(), context.venv().arg("--python").arg("3.11"), @r"
uv_snapshot!(context.filters(), context.venv().arg("--clear").arg("--python").arg("3.11"), @r"
success: true
exit_code: 0
----- stdout -----
@ -654,7 +678,7 @@ fn create_venv_respects_group_requires_python() -> Result<()> {
"#
})?;
uv_snapshot!(context.filters(), context.venv().arg("--python").arg("3.11"), @r"
uv_snapshot!(context.filters(), context.venv().arg("--clear").arg("--python").arg("3.11"), @r"
success: false
exit_code: 1
----- stdout -----
@ -981,7 +1005,9 @@ fn non_empty_dir_exists() -> Result<()> {
uv::venv::creation
× Failed to create virtualenv
The directory `.venv` exists, but it's not a virtual environment
The directory `.venv` exists.
hint: Use `--clear` to remove the directory first or `--allow-existing` to write to the directory without clearing
"###
);
@ -1011,7 +1037,9 @@ fn non_empty_dir_exists_allow_existing() -> Result<()> {
uv::venv::creation
× Failed to create virtualenv
The directory `.venv` exists, but it's not a virtual environment
The directory `.venv` exists.
hint: Use `--clear` to remove the directory first or `--allow-existing` to write to the directory without clearing
"###
);
@ -1132,31 +1160,6 @@ fn windows_shims() -> Result<()> {
Ok(())
}
#[test]
fn virtualenv_compatibility() {
let context = TestContext::new_with_versions(&["3.12"]);
// Create a virtual environment at `.venv`, passing the redundant `--clear` flag.
uv_snapshot!(context.filters(), context.venv()
.arg(context.venv.as_os_str())
.arg("--clear")
.arg("--python")
.arg("3.12"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
warning: virtualenv's `--clear` has no effect (uv always clears the virtual environment)
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
Creating virtual environment at: .venv
Activate with: source .venv/[BIN]/activate
"###
);
context.venv.assert(predicates::path::is_dir());
}
#[test]
fn verify_pyvenv_cfg() {
let context = TestContext::new("3.12");
@ -1184,6 +1187,7 @@ fn verify_pyvenv_cfg_relocatable() {
context
.venv()
.arg(context.venv.as_os_str())
.arg("--clear")
.arg("--python")
.arg("3.12")
.arg("--relocatable")

View file

@ -4554,7 +4554,7 @@ uv venv [OPTIONS] [PATH]
<h3 class="cli-reference">Options</h3>
<dl class="cli-reference"><dt id="uv-venv--allow-existing"><a href="#uv-venv--allow-existing"><code>--allow-existing</code></a></dt><dd><p>Preserve any existing files or directories at the target path.</p>
<p>By default, <code>uv venv</code> will remove an existing virtual environment at the given path, and exit with an error if the path is non-empty but <em>not</em> a virtual environment. The <code>--allow-existing</code> option will instead write to the given path, regardless of its contents, and without clearing it beforehand.</p>
<p>By default, <code>uv venv</code> will exit with an error if the given path is non-empty. The <code>--allow-existing</code> option will instead write to the given path, regardless of its contents, and without clearing it beforehand.</p>
<p>WARNING: This option can lead to unexpected behavior if the existing virtual environment and the newly-created virtual environment are linked to different Python interpreters.</p>
</dd><dt id="uv-venv--allow-insecure-host"><a href="#uv-venv--allow-insecure-host"><code>--allow-insecure-host</code></a>, <code>--trusted-host</code> <i>allow-insecure-host</i></dt><dd><p>Allow insecure connections to a host.</p>
<p>Can be provided multiple times.</p>
@ -4563,7 +4563,9 @@ uv venv [OPTIONS] [PATH]
<p>May also be set with the <code>UV_INSECURE_HOST</code> environment variable.</p></dd><dt id="uv-venv--cache-dir"><a href="#uv-venv--cache-dir"><code>--cache-dir</code></a> <i>cache-dir</i></dt><dd><p>Path to the cache directory.</p>
<p>Defaults to <code>$XDG_CACHE_HOME/uv</code> or <code>$HOME/.cache/uv</code> on macOS and Linux, and <code>%LOCALAPPDATA%\uv\cache</code> on Windows.</p>
<p>To view the location of the cache directory, run <code>uv cache dir</code>.</p>
<p>May also be set with the <code>UV_CACHE_DIR</code> environment variable.</p></dd><dt id="uv-venv--color"><a href="#uv-venv--color"><code>--color</code></a> <i>color-choice</i></dt><dd><p>Control the use of color in output.</p>
<p>May also be set with the <code>UV_CACHE_DIR</code> environment variable.</p></dd><dt id="uv-venv--clear"><a href="#uv-venv--clear"><code>--clear</code></a>, <code>-c</code></dt><dd><p>Remove any existing files or directories at the target path.</p>
<p>By default, <code>uv venv</code> will exit with an error if the given path is non-empty. The <code>--clear</code> option will instead clear a non-empty path before creating a new virtual environment.</p>
<p>May also be set with the <code>UV_VENV_CLEAR</code> environment variable.</p></dd><dt id="uv-venv--color"><a href="#uv-venv--color"><code>--color</code></a> <i>color-choice</i></dt><dd><p>Control the use of color in output.</p>
<p>By default, uv will automatically detect support for colors when writing to a terminal.</p>
<p>Possible values:</p>
<ul>

View file

@ -433,6 +433,14 @@ Equivalent to the `--torch-backend` command-line argument (e.g., `cpu`, `cu126`,
Used ephemeral environments like CI to install uv to a specific path while preventing
the installer from modifying shell profiles or environment variables.
### `UV_VENV_CLEAR`
Remove any existing files or directories at the target path.
By default, `uv venv` will exit with an error if the given path is non-empty. The
`--clear` option will instead clear a non-empty path before creating a new virtual
environment.
### `UV_VENV_SEED`
Install seed packages (one or more of: `pip`, `setuptools`, and `wheel`) into the virtual environment