Deprecate --project arg for init (#16674)
Some checks are pending
CI / cargo shear (push) Waiting to run
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | aarch64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / build binary | linux libc (push) Blocked by required conditions
CI / build binary | linux aarch64 (push) Blocked by required conditions
CI / build binary | linux musl (push) Blocked by required conditions
CI / smoke test | linux (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows x86_64 (push) Blocked by required conditions
CI / build binary | windows aarch64 (push) Blocked by required conditions
CI / build binary | msrv (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / ecosystem test | pydantic/pydantic-core (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / smoke test | linux aarch64 (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / smoke test | macos (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / integration test | activate nushell venv (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | aarch64 windows explicit (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | free-threaded python on github actions (push) Blocked by required conditions
CI / integration test | pyenv on wsl x86-64 (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / integration test | aarch64 windows implicit (push) Blocked by required conditions
CI / integration test | windows python install manager (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | pyodide on ubuntu (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | registries (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 10 (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | x86-64 python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
zizmor / Run zizmor (push) Waiting to run

Addresses https://github.com/astral-sh/uv/issues/15790

## Summary

After discussion, the functionality of `--project` vs `--directory` was
quite unclear in this case, so deprecating `--project` for `init` is
probably the clearest behavior option. This is a breaking change, so it
requires being under preview before being rolled out fully.

Included in the PR now:
- new feature flag (`init --project` is deprecated if `--preview` or
`--preview-features deprecate-project-for-init` are provided)
- tests (for `--directory` behavior, as well as the current warning and
future error)
- documentation updated in docs/concepts/projects/init.md

---------

Signed-off-by: Mikayla Thompson <mrt@mikayla.codes>
This commit is contained in:
Mikayla Thompson 2025-11-10 16:33:08 -07:00 committed by GitHub
parent 9a21897f3d
commit f22af0f88a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 146 additions and 4 deletions

View file

@ -21,6 +21,7 @@ bitflags::bitflags! {
const NATIVE_AUTH = 1 << 9;
const S3_ENDPOINT = 1 << 10;
const CACHE_SIZE = 1 << 11;
const INIT_PROJECT_FLAG = 1 << 12;
}
}
@ -42,6 +43,7 @@ impl PreviewFeatures {
Self::NATIVE_AUTH => "native-auth",
Self::S3_ENDPOINT => "s3-endpoint",
Self::CACHE_SIZE => "cache-size",
Self::INIT_PROJECT_FLAG => "init-project-flag",
_ => panic!("`flag_as_str` can only be used for exactly one feature flag"),
}
}
@ -91,6 +93,7 @@ impl FromStr for PreviewFeatures {
"native-auth" => Self::NATIVE_AUTH,
"s3-endpoint" => Self::S3_ENDPOINT,
"cache-size" => Self::CACHE_SIZE,
"init-project-flag" => Self::INIT_PROJECT_FLAG,
_ => {
warn_user_once!("Unknown preview feature: `{part}`");
continue;

View file

@ -35,6 +35,7 @@ use uv_fs::{CWD, Simplified};
#[cfg(feature = "self-update")]
use uv_pep440::release_specifiers_to_ranges;
use uv_pep508::VersionOrUrl;
use uv_preview::PreviewFeatures;
use uv_pypi_types::{ParsedDirectoryUrl, ParsedUrl};
use uv_python::PythonRequest;
use uv_requirements::{GroupsSpecification, RequirementsSource};
@ -1805,6 +1806,32 @@ async fn run_project(
let args = settings::InitSettings::resolve(args, filesystem, environment);
show_settings!(args);
// The `--project` arg is being deprecated for `init` with a warning now and an error in preview.
if explicit_project {
if globals
.preview
.is_enabled(PreviewFeatures::INIT_PROJECT_FLAG)
{
bail!(
"The `--project` option cannot be used in `uv init`. {}",
if args.path.is_some() {
"Use `--directory` instead."
} else {
"Use `--directory` or a positional path instead."
}
)
}
warn_user!(
"Use of the `--project` option in `uv init` is deprecated and will be removed in a future release. {}",
if args.path.is_some() {
"Since a positional path was provided, the `--project` option has no effect. Consider using `--directory` instead."
} else {
"Consider using `uv init <PATH>` instead."
}
);
}
// Initialize the cache.
let cache = cache.init()?;

View file

@ -4043,3 +4043,113 @@ fn git_states() {
");
assert!(!context.temp_dir.child("broken-git/.git").is_dir());
}
/// Using `uv init` with `--project` isn't allowed
#[test]
fn init_project_flag_is_not_allowed_under_preview() -> Result<()> {
let context = TestContext::new("3.12");
let child = context.temp_dir.child("foo");
child.create_dir_all()?;
// Positional `path` provided
uv_snapshot!(context.filters(), context.init().arg("--preview-features").arg("init-project-flag").arg("--project").arg("foo").arg("bar"), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: The `--project` option cannot be used in `uv init`. Use `--directory` instead.
"###);
// No positional `path` provided
uv_snapshot!(context.filters(), context.init().arg("--preview-features").arg("init-project-flag").arg("--project").arg("foo"), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: The `--project` option cannot be used in `uv init`. Use `--directory` or a positional path instead.
"###);
Ok(())
}
#[test]
fn init_project_flag_is_ignored_with_explicit_path() {
let context = TestContext::new("3.12");
// with explicit path
uv_snapshot!(context.filters(), context.init().arg("--project").arg("bar").arg("foo"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
warning: Use of the `--project` option in `uv init` is deprecated and will be removed in a future release. Since a positional path was provided, the `--project` option has no effect. Consider using `--directory` instead.
Initialized project `foo` at `[TEMP_DIR]/foo`
"###);
let pyproject = context.read("foo/pyproject.toml");
insta::with_settings!({
filters => context.filters(),
}, {
assert_snapshot!(
pyproject, @r###"
[project]
name = "foo"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
"###
);
});
}
#[test]
fn init_project_flag_is_warned_without_path() {
let context = TestContext::new("3.12");
// with explicit path
uv_snapshot!(context.filters(), context.init().arg("--project").arg("bar"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
warning: Use of the `--project` option in `uv init` is deprecated and will be removed in a future release. Consider using `uv init <PATH>` instead.
Initialized project `bar`
"###);
context
.temp_dir
.child("bar/pyproject.toml")
.assert(predicate::path::is_file());
}
/// The `--directory` flag is used as the base for path
#[test]
fn init_working_directory_change() -> Result<()> {
let context = TestContext::new("3.12");
let child = context.temp_dir.child("bar");
child.create_dir_all()?;
uv_snapshot!(context.filters(), context.init().arg("--directory").arg("bar").arg("foo"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Initialized project `foo` at `[TEMP_DIR]/bar/foo`
"###);
context
.temp_dir
.child("bar/foo/pyproject.toml")
.assert(predicate::path::is_file());
Ok(())
}

View file

@ -7831,7 +7831,7 @@ fn preview_features() {
show_settings: true,
preview: Preview {
flags: PreviewFeatures(
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE,
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | INIT_PROJECT_FLAG,
),
},
python_preference: Managed,
@ -8059,7 +8059,7 @@ fn preview_features() {
show_settings: true,
preview: Preview {
flags: PreviewFeatures(
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE,
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | INIT_PROJECT_FLAG,
),
},
python_preference: Managed,

View file

@ -9,8 +9,10 @@ flag can be used to create a project for a library instead.
## Target directory
uv will create a project in the working directory, or, in a target directory by providing a name,
e.g., `uv init foo`. If there's already a project in the target directory, i.e., if there's a
`pyproject.toml`, uv will exit with an error.
e.g., `uv init foo`. The working directory can be modified with the `--directory` option, which will
cause the target directory path will be interpreted relative to the specified working directory. If
there's already a project in the target directory, i.e., if there's a `pyproject.toml`, uv will exit
with an error.
## Applications