-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Add --script option to uv init #7404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e9c33d5
6a80b69
1523665
648bcf0
417fb26
27cdd13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -47,7 +47,17 @@ pub(crate) async fn init( | |||||
| }; | ||||||
|
|
||||||
| // Make sure a project does not already exist in the given directory. | ||||||
| if path.join("pyproject.toml").exists() { | ||||||
| if project_kind == InitProjectKind::Script { | ||||||
| if explicit_path.is_none() { | ||||||
| anyhow::bail!("Missing script name to initialize",); | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| } | ||||||
| if path.exists() { | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we use Should we add metadata to the front of the script if it doesn't exist? Or should we tackle that in a separate pull request? I think we already have machinery for this for |
||||||
| anyhow::bail!( | ||||||
| "Script is already initialized in `{}`", | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| path.display().cyan() | ||||||
| ); | ||||||
| } | ||||||
| } else 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)", | ||||||
|
|
@@ -87,7 +97,7 @@ pub(crate) async fn init( | |||||
| .await?; | ||||||
|
|
||||||
| // Create the `README.md` if it does not already exist. | ||||||
| if !no_readme { | ||||||
| if !no_readme && project_kind != InitProjectKind::Script { | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Generally, I'd use
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
| let readme = path.join("README.md"); | ||||||
| if !readme.exists() { | ||||||
| fs_err::write(readme, String::new())?; | ||||||
|
|
@@ -99,6 +109,16 @@ pub(crate) async fn init( | |||||
| None => { | ||||||
| writeln!(printer.stderr(), "Initialized project `{}`", name.cyan())?; | ||||||
| } | ||||||
| // Initialized script | ||||||
| Some(path) if project_kind == InitProjectKind::Script => { | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps we should have a match on |
||||||
| let path = | ||||||
| std::path::absolute(&path).unwrap_or_else(|_| path.simplified().to_path_buf()); | ||||||
| writeln!( | ||||||
| printer.stderr(), | ||||||
| "Initialized script at `{}`", | ||||||
| path.display().cyan() | ||||||
| )?; | ||||||
| } | ||||||
| // Initialized a project in the given directory. | ||||||
| Some(path) => { | ||||||
| let path = | ||||||
|
|
@@ -387,11 +407,12 @@ async fn init_project( | |||||
| Ok(()) | ||||||
| } | ||||||
|
|
||||||
| #[derive(Debug, Copy, Clone, Default)] | ||||||
| #[derive(Debug, Copy, Clone, Default, PartialEq)] | ||||||
| pub(crate) enum InitProjectKind { | ||||||
| #[default] | ||||||
| Application, | ||||||
| Library, | ||||||
| Script, | ||||||
| } | ||||||
|
|
||||||
| impl InitProjectKind { | ||||||
|
|
@@ -428,6 +449,9 @@ impl InitProjectKind { | |||||
| ) | ||||||
| .await | ||||||
| } | ||||||
| InitProjectKind::Script => { | ||||||
| InitProjectKind::init_script(path, requires_python, no_readme) | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -571,6 +595,35 @@ impl InitProjectKind { | |||||
|
|
||||||
| Ok(()) | ||||||
| } | ||||||
|
|
||||||
| fn init_script(path: &Path, requires_python: &RequiresPython, no_readme: bool) -> Result<()> { | ||||||
| let script_name = path | ||||||
| .file_name() | ||||||
| .and_then(|path| path.to_str()) | ||||||
| .context("Missing directory name")?; | ||||||
|
|
||||||
| // Create the embedded `pyproject.toml` | ||||||
| let pyproject = pyproject_script(script_name, requires_python, no_readme); | ||||||
|
|
||||||
| // Create the script | ||||||
| if !path.try_exists()? { | ||||||
| fs_err::write( | ||||||
| path, | ||||||
| indoc::formatdoc! {r#" | ||||||
| {pyproject} | ||||||
|
|
||||||
| def main(): | ||||||
| print("Hello from {script_name}!") | ||||||
|
|
||||||
|
|
||||||
| if __name__ == "__main__": | ||||||
| main() | ||||||
| "#}, | ||||||
| )?; | ||||||
| } | ||||||
|
|
||||||
| Ok(()) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Generate the `[project]` section of a `pyproject.toml`. | ||||||
|
|
@@ -592,6 +645,27 @@ fn pyproject_project( | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| fn pyproject_script(name: &str, requires_python: &RequiresPython, no_readme: bool) -> String { | ||||||
| let readme_txt = indoc::formatdoc! {r#" | ||||||
| # /// readme | ||||||
| # You can execute this file with any tool compliant with inline script metadata. E.g.: | ||||||
| # $ uv run {name} | ||||||
| # /// | ||||||
|
|
||||||
| "#, | ||||||
| name = name, | ||||||
| }; | ||||||
|
|
||||||
| indoc::formatdoc! {r#"{readme}# /// script | ||||||
| # requires-python = "{requires_python}" | ||||||
| # dependencies = [] | ||||||
| # /// | ||||||
| "#, | ||||||
| readme = if no_readme { "" } else { &readme_txt }, | ||||||
| requires_python = requires_python.specifiers(), | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Generate the `[build-system]` section of a `pyproject.toml`. | ||||||
| fn pyproject_build_system() -> &'static str { | ||||||
| indoc::indoc! {r#" | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Best practice is to do something like
let Some(path) = explicit_path else { bail }. However, similar to https://github.com/astral-sh/uv/pull/7404/files#r1761072383, you may want to just match onproject_kindabove on L44 when determining thepathvariable.