Skip to content

Commit

Permalink
Review feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Sep 3, 2024
1 parent efae2ee commit 4acceef
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 45 deletions.
8 changes: 8 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2184,6 +2184,14 @@ pub struct InitArgs {
#[arg(long)]
pub no_readme: bool,

/// Do not create a `.python-version` file for the project.
///
/// By default, uv will create a `.python-version` file containing the minor version of
/// the discovered Python interpreter, which will cause subsequent uv commands to use that
/// version.
#[arg(long)]
pub no_pin_python: bool,

/// Avoid discovering a workspace and create a standalone project.
///
/// By default, uv searches for workspaces in the current directory or any
Expand Down
165 changes: 124 additions & 41 deletions crates/uv/src/commands/project/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub(crate) async fn init(
package: bool,
project_kind: InitProjectKind,
no_readme: bool,
no_pin_python: bool,
python: Option<String>,
no_workspace: bool,
python_preference: PythonPreference,
Expand Down Expand Up @@ -73,6 +74,7 @@ pub(crate) async fn init(
package,
project_kind,
no_readme,
no_pin_python,
python,
no_workspace,
python_preference,
Expand Down Expand Up @@ -121,6 +123,7 @@ async fn init_project(
package: bool,
project_kind: InitProjectKind,
no_readme: bool,
no_pin_python: bool,
python: Option<String>,
no_workspace: bool,
python_preference: PythonPreference,
Expand Down Expand Up @@ -181,27 +184,67 @@ async fn init_project(
// Add a `requires-python` field to the `pyproject.toml` and return the corresponding interpreter.
let (requires_python, python_request) = if let Some(request) = python.as_deref() {
// (1) Explicit request from user
let python_request = PythonRequest::parse(request);

// Translate to a `requires-python` specifier.
let requires_python = match PythonRequest::parse(request) {
match PythonRequest::parse(request) {
PythonRequest::Version(VersionRequest::MajorMinor(major, minor)) => {
RequiresPython::greater_than_equal_version(&Version::new([
let requires_python = RequiresPython::greater_than_equal_version(&Version::new([
u64::from(major),
u64::from(minor),
]))
]));

let python_request = if no_pin_python {
None
} else {
Some(PythonRequest::Version(VersionRequest::MajorMinor(
major, minor,
)))
};

(requires_python, python_request)
}
PythonRequest::Version(VersionRequest::MajorMinorPatch(major, minor, patch)) => {
RequiresPython::greater_than_equal_version(&Version::new([
let requires_python = RequiresPython::greater_than_equal_version(&Version::new([
u64::from(major),
u64::from(minor),
u64::from(patch),
]))
]));

let python_request = if no_pin_python {
None
} else {
Some(PythonRequest::Version(VersionRequest::MajorMinorPatch(
major, minor, patch,
)))
};

(requires_python, python_request)
}
PythonRequest::Version(VersionRequest::Range(specifiers)) => {
RequiresPython::from_specifiers(&specifiers)?
ref python_request @ PythonRequest::Version(VersionRequest::Range(ref specifiers)) => {
let requires_python = RequiresPython::from_specifiers(specifiers)?;

let python_request = if no_pin_python {
None
} else {
let interpreter = PythonInstallation::find_or_download(
Some(python_request),
EnvironmentPreference::Any,
python_preference,
python_downloads,
&client_builder,
cache,
Some(&reporter),
)
.await?
.into_interpreter();

Some(PythonRequest::Version(VersionRequest::MajorMinor(
interpreter.python_major(),
interpreter.python_minor(),
)))
};

(requires_python, python_request)
}
_ => {
python_request => {
let interpreter = PythonInstallation::find_or_download(
Some(&python_request),
EnvironmentPreference::Any,
Expand All @@ -213,11 +256,22 @@ async fn init_project(
)
.await?
.into_interpreter();
RequiresPython::greater_than_equal_version(&interpreter.python_minor_version())
}
};

(requires_python, python_request)
let requires_python =
RequiresPython::greater_than_equal_version(&interpreter.python_minor_version());

let python_request = if no_pin_python {
None
} else {
Some(PythonRequest::Version(VersionRequest::MajorMinor(
interpreter.python_major(),
interpreter.python_minor(),
)))
};

(requires_python, python_request)
}
}
} else if let Some(requires_python) = workspace
.as_ref()
.and_then(|workspace| find_requires_python(workspace).ok().flatten())
Expand All @@ -226,6 +280,28 @@ async fn init_project(
let python_request =
PythonRequest::Version(VersionRequest::Range(requires_python.specifiers().clone()));

// Pin to the minor version.
let python_request = if no_pin_python {
None
} else {
let interpreter = PythonInstallation::find_or_download(
Some(&python_request),
EnvironmentPreference::Any,
python_preference,
python_downloads,
&client_builder,
cache,
Some(&reporter),
)
.await?
.into_interpreter();

Some(PythonRequest::Version(VersionRequest::MajorMinor(
interpreter.python_major(),
interpreter.python_minor(),
)))
};

(requires_python, python_request)
} else {
// (3) Default to the system Python
Expand All @@ -244,12 +320,15 @@ async fn init_project(
let requires_python =
RequiresPython::greater_than_equal_version(&interpreter.python_minor_version());

// If no `--python` is specified, pin to the patch version of the discovered interpreter.
let python_request = PythonRequest::Version(VersionRequest::MajorMinorPatch(
interpreter.python_major(),
interpreter.python_minor(),
interpreter.python_patch(),
));
// Pin to the minor version.
let python_request = if no_pin_python {
None
} else {
Some(PythonRequest::Version(VersionRequest::MajorMinor(
interpreter.python_major(),
interpreter.python_minor(),
)))
};

(requires_python, python_request)
};
Expand All @@ -259,7 +338,7 @@ async fn init_project(
name,
path,
&requires_python,
&python_request,
python_request.as_ref(),
no_readme,
package,
)
Expand Down Expand Up @@ -322,7 +401,7 @@ impl InitProjectKind {
name: &PackageName,
path: &Path,
requires_python: &RequiresPython,
python_request: &PythonRequest,
python_request: Option<&PythonRequest>,
no_readme: bool,
package: bool,
) -> Result<()> {
Expand Down Expand Up @@ -362,7 +441,7 @@ impl InitProjectKind {
name: &PackageName,
path: &Path,
requires_python: &RequiresPython,
python_request: &PythonRequest,
python_request: Option<&PythonRequest>,
no_readme: bool,
package: bool,
) -> Result<()> {
Expand Down Expand Up @@ -418,14 +497,16 @@ impl InitProjectKind {
fs_err::write(path.join("pyproject.toml"), pyproject)?;

// Write .python-version if it doesn't exist.
if PythonVersionFile::discover(path, false, false)
.await?
.is_none()
{
PythonVersionFile::new(path.join(".python-version"))
.with_versions(vec![python_request.clone()])
.write()
.await?;
if let Some(python_request) = python_request {
if PythonVersionFile::discover(path, false, false)
.await?
.is_none()
{
PythonVersionFile::new(path.join(".python-version"))
.with_versions(vec![python_request.clone()])
.write()
.await?;
}
}

Ok(())
Expand All @@ -436,7 +517,7 @@ impl InitProjectKind {
name: &PackageName,
path: &Path,
requires_python: &RequiresPython,
python_request: &PythonRequest,
python_request: Option<&PythonRequest>,
no_readme: bool,
package: bool,
) -> Result<()> {
Expand Down Expand Up @@ -469,14 +550,16 @@ impl InitProjectKind {
}

// Write .python-version if it doesn't exist.
if PythonVersionFile::discover(path, false, false)
.await?
.is_none()
{
PythonVersionFile::new(path.join(".python-version"))
.with_versions(vec![python_request.clone()])
.write()
.await?;
if let Some(python_request) = python_request {
if PythonVersionFile::discover(path, false, false)
.await?
.is_none()
{
PythonVersionFile::new(path.join(".python-version"))
.with_versions(vec![python_request.clone()])
.write()
.await?;
}
}

Ok(())
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 @@ -1028,6 +1028,7 @@ async fn run_project(
args.package,
args.kind,
args.no_readme,
args.no_pin_python,
args.python,
args.no_workspace,
globals.python_preference,
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 @@ -157,6 +157,7 @@ pub(crate) struct InitSettings {
pub(crate) package: bool,
pub(crate) kind: InitProjectKind,
pub(crate) no_readme: bool,
pub(crate) no_pin_python: bool,
pub(crate) no_workspace: bool,
pub(crate) python: Option<String>,
}
Expand All @@ -174,6 +175,7 @@ impl InitSettings {
app,
lib,
no_readme,
no_pin_python,
no_workspace,
python,
} = args;
Expand All @@ -193,6 +195,7 @@ impl InitSettings {
package,
kind,
no_readme,
no_pin_python,
no_workspace,
python,
}
Expand Down
8 changes: 4 additions & 4 deletions crates/uv/tests/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ fn init() -> Result<()> {
filters => context.filters(),
}, {
assert_snapshot!(
python_version, @"3.12.[X]"
python_version, @"3.12"
);
});

Expand Down Expand Up @@ -1633,7 +1633,7 @@ fn init_requires_python_workspace() -> Result<()> {
filters => context.filters(),
}, {
assert_snapshot!(
python_version, @">=3.10"
python_version, @"3.12"
);
});

Expand Down Expand Up @@ -1702,7 +1702,7 @@ fn init_requires_python_version() -> Result<()> {
/// specifiers verbatim.
#[test]
fn init_requires_python_specifiers() -> Result<()> {
let context = TestContext::new("3.12");
let context = TestContext::new_with_versions(&["3.8", "3.12"]);

let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! {
Expand Down Expand Up @@ -1750,7 +1750,7 @@ fn init_requires_python_specifiers() -> Result<()> {
filters => context.filters(),
}, {
assert_snapshot!(
python_version, @"==3.8.*"
python_version, @"3.8"
);
});

Expand Down
4 changes: 4 additions & 0 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,10 @@ uv init [OPTIONS] [PATH]

<p>This is the default behavior when using <code>--app</code>.</p>

</dd><dt><code>--no-pin-python</code></dt><dd><p>Do not create a <code>.python-version</code> file for the project.</p>

<p>By default, uv will create a <code>.python-version</code> file containing the minor version of the discovered Python interpreter, which will cause subsequent uv commands to use that version.</p>

</dd><dt><code>--no-progress</code></dt><dd><p>Hide all progress outputs.</p>

<p>For example, spinners or progress bars.</p>
Expand Down

0 comments on commit 4acceef

Please sign in to comment.