mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-02 18:12:17 +00:00
Add support for uv init --script
(#7565)
This PR adds support for ```uv init --script```, as defined in issue #7402 (started working on this before I saw jbvsmo's PR). Wanted to highlight a few decisions I made that differ from the existing PR: 1. ```--script``` takes a path, instead of a path/name. This potentially leads to a little ambiguity (I can certainly elaborate in the docs, lmk!), but strictly allowing ```uv init --script path/to/script.py``` felt a little more natural than allowing for ```uv init --script path/to --name script.py``` (which I also thought would prompt more questions for users, such as should the name include the .py extension?) 2. The request is processed immediately in the ```init``` method, sharing logic in resolving which python version to use with ```uv add --script```. This made more sense to me — since scripts are meant to operate in isolation, they shouldn't consider the context of an encompassing package should one exist (I also think this decision makes the relative codepaths for scripts/packages easier to follow). 3. No readme — readme felt a little excessive for a script, but I can of course add it in! --------- Co-authored-by: João Bernardo Oliveira <jbvsmo@gmail.com>
This commit is contained in:
parent
a3abd89ab0
commit
6e9ecde9c2
10 changed files with 560 additions and 131 deletions
|
@ -2301,20 +2301,21 @@ impl ExternalCommand {
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
pub struct InitArgs {
|
pub struct InitArgs {
|
||||||
/// The path to use for the project.
|
/// The path to use for the project/script.
|
||||||
///
|
///
|
||||||
/// Defaults to the current working directory. Accepts relative and absolute
|
/// Defaults to the current working directory when initializing an app or library;
|
||||||
/// paths.
|
/// required when initializing a script. Accepts relative and absolute paths.
|
||||||
///
|
///
|
||||||
/// If a `pyproject.toml` is found in any of the parent directories of the
|
/// If a `pyproject.toml` is found in any of the parent directories of the
|
||||||
/// target path, the project will be added as a workspace member of the
|
/// target path, the project will be added as a workspace member of the
|
||||||
/// parent, unless `--no-workspace` is provided.
|
/// parent, unless `--no-workspace` is provided.
|
||||||
pub path: Option<String>,
|
#[arg(required_if_eq("script", "true"))]
|
||||||
|
pub path: Option<PathBuf>,
|
||||||
|
|
||||||
/// The name of the project.
|
/// The name of the project.
|
||||||
///
|
///
|
||||||
/// Defaults to the name of the directory.
|
/// Defaults to the name of the directory.
|
||||||
#[arg(long)]
|
#[arg(long, conflicts_with = "script")]
|
||||||
pub name: Option<PackageName>,
|
pub name: Option<PackageName>,
|
||||||
|
|
||||||
/// Create a virtual project, rather than a package.
|
/// Create a virtual project, rather than a package.
|
||||||
|
@ -2351,15 +2352,27 @@ pub struct InitArgs {
|
||||||
/// By default, an application is not intended to be built and distributed as a Python package.
|
/// By default, an application is not intended to be built and distributed as a Python package.
|
||||||
/// The `--package` option can be used to create an application that is distributable, e.g., if
|
/// The `--package` option can be used to create an application that is distributable, e.g., if
|
||||||
/// you want to distribute a command-line interface via PyPI.
|
/// you want to distribute a command-line interface via PyPI.
|
||||||
#[arg(long, alias = "application", conflicts_with = "lib")]
|
#[arg(long, alias = "application", conflicts_with_all = ["lib", "script"])]
|
||||||
pub r#app: bool,
|
pub r#app: bool,
|
||||||
|
|
||||||
/// Create a project for a library.
|
/// Create a project for a library.
|
||||||
///
|
///
|
||||||
/// A library is a project that is intended to be built and distributed as a Python package.
|
/// A library is a project that is intended to be built and distributed as a Python package.
|
||||||
#[arg(long, alias = "library", conflicts_with = "app")]
|
#[arg(long, alias = "library", conflicts_with_all=["app", "script"])]
|
||||||
pub r#lib: bool,
|
pub r#lib: bool,
|
||||||
|
|
||||||
|
/// Create a script.
|
||||||
|
///
|
||||||
|
/// A script is a standalone file with embedded metadata enumerating its dependencies, along
|
||||||
|
/// with any Python version requirements, as defined in the PEP 723 specification.
|
||||||
|
///
|
||||||
|
/// PEP 723 scripts can be executed directly with `uv run`.
|
||||||
|
///
|
||||||
|
/// By default, adds a requirement on the system Python version; use `--python` to specify an
|
||||||
|
/// alternative Python version requirement.
|
||||||
|
#[arg(long, alias="script", conflicts_with_all=["app", "lib", "package"])]
|
||||||
|
pub r#script: bool,
|
||||||
|
|
||||||
/// Do not create a `README.md` file.
|
/// Do not create a `README.md` file.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub no_readme: bool,
|
pub no_readme: bool,
|
||||||
|
|
|
@ -65,7 +65,7 @@ impl Pep723Script {
|
||||||
/// Reads a Python script and generates a default PEP 723 metadata table.
|
/// Reads a Python script and generates a default PEP 723 metadata table.
|
||||||
///
|
///
|
||||||
/// See: <https://peps.python.org/pep-0723/>
|
/// See: <https://peps.python.org/pep-0723/>
|
||||||
pub async fn create(
|
pub async fn init(
|
||||||
file: impl AsRef<Path>,
|
file: impl AsRef<Path>,
|
||||||
requires_python: &VersionSpecifiers,
|
requires_python: &VersionSpecifiers,
|
||||||
) -> Result<Self, Pep723Error> {
|
) -> Result<Self, Pep723Error> {
|
||||||
|
@ -95,6 +95,51 @@ impl Pep723Script {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a PEP 723 script at the given path.
|
||||||
|
pub async fn create(
|
||||||
|
file: impl AsRef<Path>,
|
||||||
|
requires_python: &VersionSpecifiers,
|
||||||
|
existing_contents: Option<Vec<u8>>,
|
||||||
|
) -> Result<(), Pep723Error> {
|
||||||
|
let file = file.as_ref();
|
||||||
|
|
||||||
|
let script_name = file
|
||||||
|
.file_name()
|
||||||
|
.and_then(|name| name.to_str())
|
||||||
|
.ok_or_else(|| Pep723Error::InvalidFilename(file.to_string_lossy().to_string()))?;
|
||||||
|
|
||||||
|
let default_metadata = indoc::formatdoc! {r#"
|
||||||
|
requires-python = "{requires_python}"
|
||||||
|
dependencies = []
|
||||||
|
"#,
|
||||||
|
};
|
||||||
|
let metadata = serialize_metadata(&default_metadata);
|
||||||
|
|
||||||
|
let script = if let Some(existing_contents) = existing_contents {
|
||||||
|
indoc::formatdoc! {r#"
|
||||||
|
{metadata}
|
||||||
|
{content}
|
||||||
|
"#,
|
||||||
|
content = String::from_utf8(existing_contents).map_err(|err| Pep723Error::Utf8(err.utf8_error()))?}
|
||||||
|
} else {
|
||||||
|
indoc::formatdoc! {r#"
|
||||||
|
{metadata}
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
print("Hello from {name}!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
"#,
|
||||||
|
metadata = metadata,
|
||||||
|
name = script_name,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(fs_err::tokio::write(file, script).await?)
|
||||||
|
}
|
||||||
|
|
||||||
/// Replace the existing metadata in the file with new metadata and write the updated content.
|
/// Replace the existing metadata in the file with new metadata and write the updated content.
|
||||||
pub async fn write(&self, metadata: &str) -> Result<(), Pep723Error> {
|
pub async fn write(&self, metadata: &str) -> Result<(), Pep723Error> {
|
||||||
let content = format!(
|
let content = format!(
|
||||||
|
@ -161,10 +206,12 @@ pub enum Pep723Error {
|
||||||
Utf8(#[from] std::str::Utf8Error),
|
Utf8(#[from] std::str::Utf8Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Toml(#[from] toml::de::Error),
|
Toml(#[from] toml::de::Error),
|
||||||
|
#[error("Invalid filename `{0}` supplied")]
|
||||||
|
InvalidFilename(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
struct ScriptTag {
|
pub struct ScriptTag {
|
||||||
/// The content of the script before the metadata block.
|
/// The content of the script before the metadata block.
|
||||||
prelude: String,
|
prelude: String,
|
||||||
/// The metadata block.
|
/// The metadata block.
|
||||||
|
@ -202,7 +249,7 @@ impl ScriptTag {
|
||||||
/// - Postlude: `import requests\n\nprint("Hello, World!")\n`
|
/// - Postlude: `import requests\n\nprint("Hello, World!")\n`
|
||||||
///
|
///
|
||||||
/// See: <https://peps.python.org/pep-0723/>
|
/// See: <https://peps.python.org/pep-0723/>
|
||||||
fn parse(contents: &[u8]) -> Result<Option<Self>, Pep723Error> {
|
pub fn parse(contents: &[u8]) -> Result<Option<Self>, Pep723Error> {
|
||||||
// Identify the opening pragma.
|
// Identify the opening pragma.
|
||||||
let Some(index) = FINDER.find(contents) else {
|
let Some(index) = FINDER.find(contents) else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub(crate) use pip::tree::pip_tree;
|
||||||
pub(crate) use pip::uninstall::pip_uninstall;
|
pub(crate) use pip::uninstall::pip_uninstall;
|
||||||
pub(crate) use project::add::add;
|
pub(crate) use project::add::add;
|
||||||
pub(crate) use project::export::export;
|
pub(crate) use project::export::export;
|
||||||
pub(crate) use project::init::{init, InitProjectKind};
|
pub(crate) use project::init::{init, InitKind, InitProjectKind};
|
||||||
pub(crate) use project::lock::lock;
|
pub(crate) use project::lock::lock;
|
||||||
pub(crate) use project::remove::remove;
|
pub(crate) use project::remove::remove;
|
||||||
pub(crate) use project::run::{run, RunCommand};
|
pub(crate) use project::run::{run, RunCommand};
|
||||||
|
|
|
@ -29,7 +29,7 @@ use uv_python::{
|
||||||
PythonPreference, PythonRequest, PythonVersionFile, VersionRequest,
|
PythonPreference, PythonRequest, PythonVersionFile, VersionRequest,
|
||||||
};
|
};
|
||||||
use uv_requirements::{NamedRequirementsResolver, RequirementsSource, RequirementsSpecification};
|
use uv_requirements::{NamedRequirementsResolver, RequirementsSource, RequirementsSpecification};
|
||||||
use uv_resolver::{FlatIndex, RequiresPython};
|
use uv_resolver::FlatIndex;
|
||||||
use uv_scripts::Pep723Script;
|
use uv_scripts::Pep723Script;
|
||||||
use uv_types::{BuildIsolation, HashStrategy};
|
use uv_types::{BuildIsolation, HashStrategy};
|
||||||
use uv_warnings::warn_user_once;
|
use uv_warnings::warn_user_once;
|
||||||
|
@ -42,7 +42,7 @@ use crate::commands::pip::loggers::{
|
||||||
};
|
};
|
||||||
use crate::commands::pip::operations::Modifications;
|
use crate::commands::pip::operations::Modifications;
|
||||||
use crate::commands::pip::resolution_environment;
|
use crate::commands::pip::resolution_environment;
|
||||||
use crate::commands::project::ProjectError;
|
use crate::commands::project::{script_python_requirement, ProjectError};
|
||||||
use crate::commands::reporters::{PythonDownloadReporter, ResolverReporter};
|
use crate::commands::reporters::{PythonDownloadReporter, ResolverReporter};
|
||||||
use crate::commands::{pip, project, ExitStatus, SharedState};
|
use crate::commands::{pip, project, ExitStatus, SharedState};
|
||||||
use crate::printer::Printer;
|
use crate::printer::Printer;
|
||||||
|
@ -129,35 +129,18 @@ pub(crate) async fn add(
|
||||||
let script = if let Some(script) = Pep723Script::read(&script).await? {
|
let script = if let Some(script) = Pep723Script::read(&script).await? {
|
||||||
script
|
script
|
||||||
} else {
|
} else {
|
||||||
let python_request = if let Some(request) = python.as_deref() {
|
let requires_python = script_python_requirement(
|
||||||
// (1) Explicit request from user
|
python.as_deref(),
|
||||||
PythonRequest::parse(request)
|
project_dir,
|
||||||
} else if let Some(request) = PythonVersionFile::discover(project_dir, false, false)
|
false,
|
||||||
.await?
|
|
||||||
.and_then(PythonVersionFile::into_version)
|
|
||||||
{
|
|
||||||
// (2) Request from `.python-version`
|
|
||||||
request
|
|
||||||
} else {
|
|
||||||
// (3) Assume any Python version
|
|
||||||
PythonRequest::Default
|
|
||||||
};
|
|
||||||
|
|
||||||
let interpreter = PythonInstallation::find_or_download(
|
|
||||||
Some(&python_request),
|
|
||||||
EnvironmentPreference::Any,
|
|
||||||
python_preference,
|
python_preference,
|
||||||
python_downloads,
|
python_downloads,
|
||||||
&client_builder,
|
&client_builder,
|
||||||
cache,
|
cache,
|
||||||
Some(&reporter),
|
&reporter,
|
||||||
)
|
)
|
||||||
.await?
|
.await?;
|
||||||
.into_interpreter();
|
Pep723Script::init(&script, requires_python.specifiers()).await?
|
||||||
|
|
||||||
let requires_python =
|
|
||||||
RequiresPython::greater_than_equal_version(&interpreter.python_minor_version());
|
|
||||||
Pep723Script::create(&script, requires_python.specifiers()).await?
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let python_request = if let Some(request) = python.as_deref() {
|
let python_request = if let Some(request) = python.as_deref() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
|
@ -8,16 +8,18 @@ use pep508_rs::PackageName;
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::{BaseClientBuilder, Connectivity};
|
use uv_client::{BaseClientBuilder, Connectivity};
|
||||||
use uv_fs::Simplified;
|
use uv_fs::{Simplified, CWD};
|
||||||
use uv_python::{
|
use uv_python::{
|
||||||
EnvironmentPreference, PythonDownloads, PythonInstallation, PythonPreference, PythonRequest,
|
EnvironmentPreference, PythonDownloads, PythonInstallation, PythonPreference, PythonRequest,
|
||||||
PythonVersionFile, VersionRequest,
|
PythonVersionFile, VersionRequest,
|
||||||
};
|
};
|
||||||
use uv_resolver::RequiresPython;
|
use uv_resolver::RequiresPython;
|
||||||
|
use uv_scripts::{Pep723Script, ScriptTag};
|
||||||
|
use uv_warnings::warn_user_once;
|
||||||
use uv_workspace::pyproject_mut::{DependencyTarget, PyProjectTomlMut};
|
use uv_workspace::pyproject_mut::{DependencyTarget, PyProjectTomlMut};
|
||||||
use uv_workspace::{DiscoveryOptions, MemberDiscovery, Workspace, WorkspaceError};
|
use uv_workspace::{DiscoveryOptions, MemberDiscovery, Workspace, WorkspaceError};
|
||||||
|
|
||||||
use crate::commands::project::find_requires_python;
|
use crate::commands::project::{find_requires_python, script_python_requirement};
|
||||||
use crate::commands::reporters::PythonDownloadReporter;
|
use crate::commands::reporters::PythonDownloadReporter;
|
||||||
use crate::commands::ExitStatus;
|
use crate::commands::ExitStatus;
|
||||||
use crate::printer::Printer;
|
use crate::printer::Printer;
|
||||||
|
@ -26,10 +28,10 @@ use crate::printer::Printer;
|
||||||
#[allow(clippy::single_match_else, clippy::fn_params_excessive_bools)]
|
#[allow(clippy::single_match_else, clippy::fn_params_excessive_bools)]
|
||||||
pub(crate) async fn init(
|
pub(crate) async fn init(
|
||||||
project_dir: &Path,
|
project_dir: &Path,
|
||||||
explicit_path: Option<String>,
|
explicit_path: Option<PathBuf>,
|
||||||
name: Option<PackageName>,
|
name: Option<PackageName>,
|
||||||
package: bool,
|
package: bool,
|
||||||
project_kind: InitProjectKind,
|
init_kind: InitKind,
|
||||||
no_readme: bool,
|
no_readme: bool,
|
||||||
no_pin_python: bool,
|
no_pin_python: bool,
|
||||||
python: Option<String>,
|
python: Option<String>,
|
||||||
|
@ -41,81 +43,187 @@ pub(crate) async fn init(
|
||||||
cache: &Cache,
|
cache: &Cache,
|
||||||
printer: Printer,
|
printer: Printer,
|
||||||
) -> Result<ExitStatus> {
|
) -> Result<ExitStatus> {
|
||||||
// Default to the current directory if a path was not provided.
|
match init_kind {
|
||||||
let path = match explicit_path {
|
InitKind::Script => {
|
||||||
None => project_dir.to_path_buf(),
|
let Some(path) = explicit_path.as_deref() else {
|
||||||
Some(ref path) => std::path::absolute(path)?,
|
anyhow::bail!("Script initialization requires a file path")
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make sure a project does not already exist in the given directory.
|
init_script(
|
||||||
if path.join("pyproject.toml").exists() {
|
path,
|
||||||
let path = std::path::absolute(&path).unwrap_or_else(|_| path.simplified().to_path_buf());
|
python,
|
||||||
anyhow::bail!(
|
connectivity,
|
||||||
"Project is already initialized in `{}` (`pyproject.toml` file exists)",
|
python_preference,
|
||||||
path.display().cyan()
|
python_downloads,
|
||||||
);
|
cache,
|
||||||
}
|
printer,
|
||||||
|
no_workspace,
|
||||||
|
no_readme,
|
||||||
|
no_pin_python,
|
||||||
|
package,
|
||||||
|
native_tls,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Default to the directory name if a name was not provided.
|
|
||||||
let name = match name {
|
|
||||||
Some(name) => name,
|
|
||||||
None => {
|
|
||||||
let name = path
|
|
||||||
.file_name()
|
|
||||||
.and_then(|path| path.to_str())
|
|
||||||
.context("Missing directory name")?;
|
|
||||||
|
|
||||||
PackageName::new(name.to_string())?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
init_project(
|
|
||||||
&path,
|
|
||||||
&name,
|
|
||||||
package,
|
|
||||||
project_kind,
|
|
||||||
no_readme,
|
|
||||||
no_pin_python,
|
|
||||||
python,
|
|
||||||
no_workspace,
|
|
||||||
python_preference,
|
|
||||||
python_downloads,
|
|
||||||
connectivity,
|
|
||||||
native_tls,
|
|
||||||
cache,
|
|
||||||
printer,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Create the `README.md` if it does not already exist.
|
|
||||||
if !no_readme {
|
|
||||||
let readme = path.join("README.md");
|
|
||||||
if !readme.exists() {
|
|
||||||
fs_err::write(readme, String::new())?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match explicit_path {
|
|
||||||
// Initialized a project in the current directory.
|
|
||||||
None => {
|
|
||||||
writeln!(printer.stderr(), "Initialized project `{}`", name.cyan())?;
|
|
||||||
}
|
|
||||||
// Initialized a project in the given directory.
|
|
||||||
Some(path) => {
|
|
||||||
let path =
|
|
||||||
std::path::absolute(&path).unwrap_or_else(|_| path.simplified().to_path_buf());
|
|
||||||
writeln!(
|
writeln!(
|
||||||
printer.stderr(),
|
printer.stderr(),
|
||||||
"Initialized project `{}` at `{}`",
|
"Initialized script at `{}`",
|
||||||
name.cyan(),
|
path.user_display().cyan()
|
||||||
path.display().cyan()
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
InitKind::Project(project_kind) => {
|
||||||
|
// Default to the current directory if a path was not provided.
|
||||||
|
let path = match explicit_path {
|
||||||
|
None => project_dir.to_path_buf(),
|
||||||
|
Some(ref path) => std::path::absolute(path)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make sure a project does not already exist in the given directory.
|
||||||
|
if path.join("pyproject.toml").exists() {
|
||||||
|
let path =
|
||||||
|
std::path::absolute(&path).unwrap_or_else(|_| path.simplified().to_path_buf());
|
||||||
|
anyhow::bail!(
|
||||||
|
"Project is already initialized in `{}` (`pyproject.toml` file exists)",
|
||||||
|
path.display().cyan()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to the directory name if a name was not provided.
|
||||||
|
let name = match name {
|
||||||
|
Some(name) => name,
|
||||||
|
None => {
|
||||||
|
let name = path
|
||||||
|
.file_name()
|
||||||
|
.and_then(|path| path.to_str())
|
||||||
|
.context("Missing directory name")?;
|
||||||
|
|
||||||
|
PackageName::new(name.to_string())?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
init_project(
|
||||||
|
&path,
|
||||||
|
&name,
|
||||||
|
package,
|
||||||
|
project_kind,
|
||||||
|
no_readme,
|
||||||
|
no_pin_python,
|
||||||
|
python,
|
||||||
|
no_workspace,
|
||||||
|
python_preference,
|
||||||
|
python_downloads,
|
||||||
|
connectivity,
|
||||||
|
native_tls,
|
||||||
|
cache,
|
||||||
|
printer,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Create the `README.md` if it does not already exist.
|
||||||
|
if !no_readme {
|
||||||
|
let readme = path.join("README.md");
|
||||||
|
if !readme.exists() {
|
||||||
|
fs_err::write(readme, String::new())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match explicit_path {
|
||||||
|
// Initialized a project in the current directory.
|
||||||
|
None => {
|
||||||
|
writeln!(printer.stderr(), "Initialized project `{}`", name.cyan())?;
|
||||||
|
}
|
||||||
|
// Initialized a project in the given directory.
|
||||||
|
Some(path) => {
|
||||||
|
let path = std::path::absolute(&path)
|
||||||
|
.unwrap_or_else(|_| path.simplified().to_path_buf());
|
||||||
|
writeln!(
|
||||||
|
printer.stderr(),
|
||||||
|
"Initialized project `{}` at `{}`",
|
||||||
|
name.cyan(),
|
||||||
|
path.display().cyan()
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ExitStatus::Success)
|
Ok(ExitStatus::Success)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::fn_params_excessive_bools)]
|
||||||
|
async fn init_script(
|
||||||
|
script_path: &Path,
|
||||||
|
python: Option<String>,
|
||||||
|
connectivity: Connectivity,
|
||||||
|
python_preference: PythonPreference,
|
||||||
|
python_downloads: PythonDownloads,
|
||||||
|
cache: &Cache,
|
||||||
|
printer: Printer,
|
||||||
|
no_workspace: bool,
|
||||||
|
no_readme: bool,
|
||||||
|
no_pin_python: bool,
|
||||||
|
package: bool,
|
||||||
|
native_tls: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
if no_workspace {
|
||||||
|
warn_user_once!("`--no_workspace` is a no-op for Python scripts, which are standalone");
|
||||||
|
}
|
||||||
|
if no_readme {
|
||||||
|
warn_user_once!("`--no_readme` is a no-op for Python scripts, which are standalone");
|
||||||
|
}
|
||||||
|
if package {
|
||||||
|
warn_user_once!("`--package` is a no-op for Python scripts, which are standalone");
|
||||||
|
}
|
||||||
|
let client_builder = BaseClientBuilder::new()
|
||||||
|
.connectivity(connectivity)
|
||||||
|
.native_tls(native_tls);
|
||||||
|
|
||||||
|
let reporter = PythonDownloadReporter::single(printer);
|
||||||
|
|
||||||
|
// If the file already exists, read its content.
|
||||||
|
let content = match fs_err::tokio::read(script_path).await {
|
||||||
|
Ok(metadata) => {
|
||||||
|
// If the file is already a script, raise an error.
|
||||||
|
if ScriptTag::parse(&metadata)?.is_some() {
|
||||||
|
anyhow::bail!(
|
||||||
|
"`{}` is already a PEP 723 script; use `{}` to execute it",
|
||||||
|
script_path.simplified_display().cyan(),
|
||||||
|
"uv run".green()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(metadata)
|
||||||
|
}
|
||||||
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => None,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(anyhow::Error::from(err).context(format!(
|
||||||
|
"Failed to read script at `{}`",
|
||||||
|
script_path.simplified_display().cyan()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let requires_python = script_python_requirement(
|
||||||
|
python.as_deref(),
|
||||||
|
&CWD,
|
||||||
|
no_pin_python,
|
||||||
|
python_preference,
|
||||||
|
python_downloads,
|
||||||
|
&client_builder,
|
||||||
|
cache,
|
||||||
|
&reporter,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if let Some(parent) = script_path.parent() {
|
||||||
|
fs_err::tokio::create_dir_all(parent).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pep723Script::create(script_path, requires_python.specifiers(), content).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize a project (and, implicitly, a workspace root) at the given path.
|
/// Initialize a project (and, implicitly, a workspace root) at the given path.
|
||||||
#[allow(clippy::fn_params_excessive_bools)]
|
#[allow(clippy::fn_params_excessive_bools)]
|
||||||
async fn init_project(
|
async fn init_project(
|
||||||
|
@ -395,13 +503,38 @@ async fn init_project(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The kind of entity to initialize (either a PEP 723 script or a Python project).
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub(crate) enum InitKind {
|
||||||
|
/// Initialize a Python project.
|
||||||
|
Project(InitProjectKind),
|
||||||
|
/// Initialize a PEP 723 script.
|
||||||
|
Script,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for InitKind {
|
||||||
|
fn default() -> Self {
|
||||||
|
InitKind::Project(InitProjectKind::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The kind of Python project to initialize (either an application or a library).
|
||||||
#[derive(Debug, Copy, Clone, Default)]
|
#[derive(Debug, Copy, Clone, Default)]
|
||||||
pub(crate) enum InitProjectKind {
|
pub(crate) enum InitProjectKind {
|
||||||
|
/// Initialize a Python application.
|
||||||
#[default]
|
#[default]
|
||||||
Application,
|
Application,
|
||||||
|
/// Initialize a Python library.
|
||||||
Library,
|
Library,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl InitKind {
|
||||||
|
/// Returns `true` if the project should be packaged by default.
|
||||||
|
pub(crate) fn packaged_by_default(self) -> bool {
|
||||||
|
matches!(self, InitKind::Project(InitProjectKind::Library))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl InitProjectKind {
|
impl InitProjectKind {
|
||||||
/// Initialize this project kind at the target path.
|
/// Initialize this project kind at the target path.
|
||||||
async fn init(
|
async fn init(
|
||||||
|
@ -439,11 +572,6 @@ impl InitProjectKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this project kind is packaged by default.
|
|
||||||
pub(crate) fn packaged_by_default(self) -> bool {
|
|
||||||
matches!(self, InitProjectKind::Library)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn init_application(
|
async fn init_application(
|
||||||
self,
|
self,
|
||||||
name: &PackageName,
|
name: &PackageName,
|
||||||
|
@ -588,13 +716,13 @@ fn pyproject_project(
|
||||||
no_readme: bool,
|
no_readme: bool,
|
||||||
) -> String {
|
) -> String {
|
||||||
indoc::formatdoc! {r#"
|
indoc::formatdoc! {r#"
|
||||||
[project]
|
[project]
|
||||||
name = "{name}"
|
name = "{name}"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Add your description here"{readme}
|
description = "Add your description here"{readme}
|
||||||
requires-python = "{requires_python}"
|
requires-python = "{requires_python}"
|
||||||
dependencies = []
|
dependencies = []
|
||||||
"#,
|
"#,
|
||||||
readme = if no_readme { "" } else { "\nreadme = \"README.md\"" },
|
readme = if no_readme { "" } else { "\nreadme = \"README.md\"" },
|
||||||
requires_python = requires_python.specifiers(),
|
requires_python = requires_python.specifiers(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
|
@ -1265,6 +1265,50 @@ pub(crate) async fn update_environment(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determine the [`RequiresPython`] requirement for a PEP 723 script.
|
||||||
|
pub(crate) async fn script_python_requirement(
|
||||||
|
python: Option<&str>,
|
||||||
|
directory: &Path,
|
||||||
|
no_pin_python: bool,
|
||||||
|
python_preference: PythonPreference,
|
||||||
|
python_downloads: PythonDownloads,
|
||||||
|
client_builder: &BaseClientBuilder<'_>,
|
||||||
|
cache: &Cache,
|
||||||
|
reporter: &PythonDownloadReporter,
|
||||||
|
) -> anyhow::Result<RequiresPython> {
|
||||||
|
let python_request = if let Some(request) = python {
|
||||||
|
// (1) Explicit request from user
|
||||||
|
PythonRequest::parse(request)
|
||||||
|
} else if let (false, Some(request)) = (
|
||||||
|
no_pin_python,
|
||||||
|
PythonVersionFile::discover(directory, false, false)
|
||||||
|
.await?
|
||||||
|
.and_then(PythonVersionFile::into_version),
|
||||||
|
) {
|
||||||
|
// (2) Request from `.python-version`
|
||||||
|
request
|
||||||
|
} else {
|
||||||
|
// (3) Assume any Python version
|
||||||
|
PythonRequest::Any
|
||||||
|
};
|
||||||
|
|
||||||
|
let interpreter = PythonInstallation::find_or_download(
|
||||||
|
Some(&python_request),
|
||||||
|
EnvironmentPreference::Any,
|
||||||
|
python_preference,
|
||||||
|
python_downloads,
|
||||||
|
client_builder,
|
||||||
|
cache,
|
||||||
|
Some(reporter),
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.into_interpreter();
|
||||||
|
|
||||||
|
Ok(RequiresPython::greater_than_equal_version(
|
||||||
|
&interpreter.python_minor_version(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// Warn if the user provides (e.g.) an `--index-url` in a requirements file.
|
/// Warn if the user provides (e.g.) an `--index-url` in a requirements file.
|
||||||
fn warn_on_requirements_txt_setting(
|
fn warn_on_requirements_txt_setting(
|
||||||
spec: &RequirementsSpecification,
|
spec: &RequirementsSpecification,
|
||||||
|
|
|
@ -39,7 +39,7 @@ use uv_warnings::warn_user_once;
|
||||||
use uv_workspace::pyproject::DependencyType;
|
use uv_workspace::pyproject::DependencyType;
|
||||||
|
|
||||||
use crate::commands::ToolRunCommand;
|
use crate::commands::ToolRunCommand;
|
||||||
use crate::commands::{pip::operations::Modifications, InitProjectKind};
|
use crate::commands::{pip::operations::Modifications, InitKind, InitProjectKind};
|
||||||
|
|
||||||
/// The default publish URL.
|
/// The default publish URL.
|
||||||
const PYPI_PUBLISH_URL: &str = "https://upload.pypi.org/legacy/";
|
const PYPI_PUBLISH_URL: &str = "https://upload.pypi.org/legacy/";
|
||||||
|
@ -158,10 +158,10 @@ impl CacheSettings {
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct InitSettings {
|
pub(crate) struct InitSettings {
|
||||||
pub(crate) path: Option<String>,
|
pub(crate) path: Option<PathBuf>,
|
||||||
pub(crate) name: Option<PackageName>,
|
pub(crate) name: Option<PackageName>,
|
||||||
pub(crate) package: bool,
|
pub(crate) package: bool,
|
||||||
pub(crate) kind: InitProjectKind,
|
pub(crate) kind: InitKind,
|
||||||
pub(crate) no_readme: bool,
|
pub(crate) no_readme: bool,
|
||||||
pub(crate) no_pin_python: bool,
|
pub(crate) no_pin_python: bool,
|
||||||
pub(crate) no_workspace: bool,
|
pub(crate) no_workspace: bool,
|
||||||
|
@ -180,17 +180,19 @@ impl InitSettings {
|
||||||
no_package,
|
no_package,
|
||||||
app,
|
app,
|
||||||
lib,
|
lib,
|
||||||
|
script,
|
||||||
no_readme,
|
no_readme,
|
||||||
no_pin_python,
|
no_pin_python,
|
||||||
no_workspace,
|
no_workspace,
|
||||||
python,
|
python,
|
||||||
} = args;
|
} = args;
|
||||||
|
|
||||||
let kind = match (app, lib) {
|
let kind = match (app, lib, script) {
|
||||||
(true, false) => InitProjectKind::Application,
|
(true, false, false) => InitKind::Project(InitProjectKind::Application),
|
||||||
(false, true) => InitProjectKind::Library,
|
(false, true, false) => InitKind::Project(InitProjectKind::Library),
|
||||||
(false, false) => InitProjectKind::default(),
|
(false, false, true) => InitKind::Script,
|
||||||
(true, true) => unreachable!("`app` and `lib` are mutually exclusive"),
|
(false, false, false) => InitKind::default(),
|
||||||
|
(_, _, _) => unreachable!("`app`, `lib`, and `script` are mutually exclusive"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let package = flag(package || r#virtual, no_package).unwrap_or(kind.packaged_by_default());
|
let package = flag(package || r#virtual, no_package).unwrap_or(kind.packaged_by_default());
|
||||||
|
|
|
@ -401,6 +401,201 @@ fn init_library() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// General init --script correctness test
|
||||||
|
#[test]
|
||||||
|
fn init_script() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let child = context.temp_dir.child("foo");
|
||||||
|
child.create_dir_all()?;
|
||||||
|
|
||||||
|
let script = child.join("hello.py");
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.init().current_dir(&child).arg("--script").arg("hello.py"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Initialized script at `hello.py`
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let script = fs_err::read_to_string(&script)?;
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
script, @r###"
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.12"
|
||||||
|
# dependencies = []
|
||||||
|
# ///
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
print("Hello from hello.py!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.run().current_dir(&child).arg("python").arg("hello.py"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
Hello from hello.py!
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure python versions passed as arguments are present in file metadata
|
||||||
|
#[test]
|
||||||
|
fn init_script_python_version() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.11");
|
||||||
|
|
||||||
|
let child = context.temp_dir.child("foo");
|
||||||
|
child.create_dir_all()?;
|
||||||
|
|
||||||
|
let script = child.join("version.py");
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.init().current_dir(&child).arg("--script").arg("version.py").arg("--python").arg("3.11"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Initialized script at `version.py`
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let script = fs_err::read_to_string(&script)?;
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
script, @r###"
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.11"
|
||||||
|
# dependencies = []
|
||||||
|
# ///
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
print("Hello from version.py!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init script should create parent directories if they don't exist
|
||||||
|
#[test]
|
||||||
|
fn init_script_create_directory() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let child = context.temp_dir.child("foo");
|
||||||
|
child.create_dir_all()?;
|
||||||
|
|
||||||
|
let script = child.join("test").join("dir.py");
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.init().current_dir(&child).arg("--script").arg("test/dir.py"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Initialized script at `test/dir.py`
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let script = fs_err::read_to_string(&script)?;
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
script, @r###"
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.12"
|
||||||
|
# dependencies = []
|
||||||
|
# ///
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
print("Hello from dir.py!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init script should fail if file is already a PEP 723 script
|
||||||
|
#[test]
|
||||||
|
fn init_script_file_conflicts() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let child = context.temp_dir.child("foo");
|
||||||
|
child.create_dir_all()?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.init().current_dir(&child).arg("--script").arg("name_conflict.py"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Initialized script at `name_conflict.py`
|
||||||
|
"###);
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.init().current_dir(&child).arg("--script").arg("name_conflict.py"), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: `name_conflict.py` is already a PEP 723 script; use `uv run` to execute it
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let contents = "print(\"Hello, world!\")";
|
||||||
|
fs_err::write(child.join("existing_script.py"), contents)?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.init().current_dir(&child).arg("--script").arg("existing_script.py"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Initialized script at `existing_script.py`
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let existing_script = fs_err::read_to_string(child.join("existing_script.py"))?;
|
||||||
|
|
||||||
|
assert_snapshot!(
|
||||||
|
existing_script, @r###"
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.12"
|
||||||
|
# dependencies = []
|
||||||
|
# ///
|
||||||
|
|
||||||
|
print("Hello, world!")
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Run `uv init --lib` with an existing py.typed file
|
/// Run `uv init --lib` with an existing py.typed file
|
||||||
#[test]
|
#[test]
|
||||||
fn init_py_typed_exists() -> Result<()> {
|
fn init_py_typed_exists() -> Result<()> {
|
||||||
|
|
|
@ -127,11 +127,20 @@ Multiple dependencies can be requested by repeating with `--with` option.
|
||||||
Note that if `uv run` is used in a _project_, these dependencies will be included _in addition_ to
|
Note that if `uv run` is used in a _project_, these dependencies will be included _in addition_ to
|
||||||
the project's dependencies. To opt-out of this behavior, use the `--no-project` flag.
|
the project's dependencies. To opt-out of this behavior, use the `--no-project` flag.
|
||||||
|
|
||||||
## Declaring script dependencies
|
## Creating a Python script
|
||||||
|
|
||||||
Python recently added a standard format for
|
Python recently added a standard format for
|
||||||
[inline script metadata](https://packaging.python.org/en/latest/specifications/inline-script-metadata/#inline-script-metadata).
|
[inline script metadata](https://packaging.python.org/en/latest/specifications/inline-script-metadata/#inline-script-metadata).
|
||||||
This allows the dependencies for a script to be declared in the script itself.
|
It allows for selecting Python versions and defining dependencies. Use `uv init --script` to
|
||||||
|
initialize scripts with the inline metadata:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ uv init --script example.py --python 3.12
|
||||||
|
```
|
||||||
|
|
||||||
|
## Declaring script dependencies
|
||||||
|
|
||||||
|
The inline metadata format allows the dependencies for a script to be declared in the script itself.
|
||||||
|
|
||||||
uv supports adding and updating inline script metadata for you. Use `uv add --script` to declare the
|
uv supports adding and updating inline script metadata for you. Use `uv add --script` to declare the
|
||||||
dependencies for the script:
|
dependencies for the script:
|
||||||
|
|
|
@ -412,9 +412,9 @@ uv init [OPTIONS] [PATH]
|
||||||
|
|
||||||
<h3 class="cli-reference">Arguments</h3>
|
<h3 class="cli-reference">Arguments</h3>
|
||||||
|
|
||||||
<dl class="cli-reference"><dt><code>PATH</code></dt><dd><p>The path to use for the project.</p>
|
<dl class="cli-reference"><dt><code>PATH</code></dt><dd><p>The path to use for the project/script.</p>
|
||||||
|
|
||||||
<p>Defaults to the current working directory. Accepts relative and absolute paths.</p>
|
<p>Defaults to the current working directory when initializing an app or library; required when initializing a script. Accepts relative and absolute paths.</p>
|
||||||
|
|
||||||
<p>If a <code>pyproject.toml</code> is found in any of the parent directories of the target path, the project will be added as a workspace member of the parent, unless <code>--no-workspace</code> is provided.</p>
|
<p>If a <code>pyproject.toml</code> is found in any of the parent directories of the target path, the project will be added as a workspace member of the parent, unless <code>--no-workspace</code> is provided.</p>
|
||||||
|
|
||||||
|
@ -550,6 +550,14 @@ uv init [OPTIONS] [PATH]
|
||||||
</ul>
|
</ul>
|
||||||
</dd><dt><code>--quiet</code>, <code>-q</code></dt><dd><p>Do not print any output</p>
|
</dd><dt><code>--quiet</code>, <code>-q</code></dt><dd><p>Do not print any output</p>
|
||||||
|
|
||||||
|
</dd><dt><code>--script</code></dt><dd><p>Create a script.</p>
|
||||||
|
|
||||||
|
<p>A script is a standalone file with embedded metadata enumerating its dependencies, along with any Python version requirements, as defined in the PEP 723 specification.</p>
|
||||||
|
|
||||||
|
<p>PEP 723 scripts can be executed directly with <code>uv run</code>.</p>
|
||||||
|
|
||||||
|
<p>By default, adds a requirement on the system Python version; use <code>--python</code> to specify an alternative Python version requirement.</p>
|
||||||
|
|
||||||
</dd><dt><code>--verbose</code>, <code>-v</code></dt><dd><p>Use verbose output.</p>
|
</dd><dt><code>--verbose</code>, <code>-v</code></dt><dd><p>Use verbose output.</p>
|
||||||
|
|
||||||
<p>You can configure fine-grained logging using the <code>RUST_LOG</code> environment variable. (<https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives>)</p>
|
<p>You can configure fine-grained logging using the <code>RUST_LOG</code> environment variable. (<https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives>)</p>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue