mirror of https://github.com/astral-sh/uv
Add support for Windows legacy scripts via uv tool run (#12079)
## Summary Follow up to https://github.com/astral-sh/uv/pull/11888 with added support for uv tool run. Changes * Added functionality for running windows scripts in previous PR was moved from run.rs to uv_shell::runnable. * EXE was added as a supported type, this simplified integration across both uv run and uvx while retaining a backwards compatible behavior and properly prioritizing .exe over others. Name was adjusted to runnable as a result to better represent intent. ## Test Plan New tests added. ## Documentation Added new documentation.
This commit is contained in:
parent
82212bb439
commit
e096ab2411
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub mod runnable;
|
||||||
mod shlex;
|
mod shlex;
|
||||||
pub mod windows;
|
pub mod windows;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
//! Utilities for running executables and scripts. Particularly in Windows.
|
||||||
|
|
||||||
|
use std::env::consts::EXE_EXTENSION;
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum WindowsRunnable {
|
||||||
|
/// Windows PE (.exe)
|
||||||
|
Executable,
|
||||||
|
/// `PowerShell` script (.ps1)
|
||||||
|
PowerShell,
|
||||||
|
/// Command Prompt NT script (.cmd)
|
||||||
|
Command,
|
||||||
|
/// Command Prompt script (.bat)
|
||||||
|
Batch,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowsRunnable {
|
||||||
|
/// Returns a list of all supported Windows runnable types.
|
||||||
|
fn all() -> &'static [Self] {
|
||||||
|
&[
|
||||||
|
Self::Executable,
|
||||||
|
Self::PowerShell,
|
||||||
|
Self::Command,
|
||||||
|
Self::Batch,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the extension for a given Windows runnable type.
|
||||||
|
fn to_extension(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Executable => EXE_EXTENSION,
|
||||||
|
Self::PowerShell => "ps1",
|
||||||
|
Self::Command => "cmd",
|
||||||
|
Self::Batch => "bat",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines the runnable type from a given Windows file extension.
|
||||||
|
fn from_extension(ext: &str) -> Option<Self> {
|
||||||
|
match ext {
|
||||||
|
EXE_EXTENSION => Some(Self::Executable),
|
||||||
|
"ps1" => Some(Self::PowerShell),
|
||||||
|
"cmd" => Some(Self::Command),
|
||||||
|
"bat" => Some(Self::Batch),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [`Command`] to run the given type under the appropriate Windows runtime.
|
||||||
|
fn as_command(&self, runnable_path: &Path) -> Command {
|
||||||
|
match self {
|
||||||
|
Self::Executable => Command::new(runnable_path),
|
||||||
|
Self::PowerShell => {
|
||||||
|
let mut cmd = Command::new("powershell");
|
||||||
|
cmd.arg("-NoLogo").arg("-File").arg(runnable_path);
|
||||||
|
cmd
|
||||||
|
}
|
||||||
|
Self::Command | Self::Batch => {
|
||||||
|
let mut cmd = Command::new("cmd");
|
||||||
|
cmd.arg("/q").arg("/c").arg(runnable_path);
|
||||||
|
cmd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle console and legacy setuptools scripts for Windows.
|
||||||
|
///
|
||||||
|
/// Returns [`Command`] that can be used to invoke a supported runnable on Windows
|
||||||
|
/// under the scripts path of an interpreter environment.
|
||||||
|
pub fn from_script_path(script_path: &Path, runnable_name: &OsStr) -> Command {
|
||||||
|
let script_path = script_path.join(runnable_name);
|
||||||
|
|
||||||
|
// Honor explicit extension if provided and recognized.
|
||||||
|
if let Some(script_type) = script_path
|
||||||
|
.extension()
|
||||||
|
.and_then(OsStr::to_str)
|
||||||
|
.and_then(Self::from_extension)
|
||||||
|
.filter(|_| script_path.is_file())
|
||||||
|
{
|
||||||
|
return script_type.as_command(&script_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guess the extension when an explicit one is not provided.
|
||||||
|
// We also add the extension when missing since for some types (e.g. PowerShell) it must be explicit.
|
||||||
|
Self::all()
|
||||||
|
.iter()
|
||||||
|
.map(|script_type| {
|
||||||
|
(
|
||||||
|
script_type,
|
||||||
|
script_path.with_extension(script_type.to_extension()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.find(|(_, script_path)| script_path.is_file())
|
||||||
|
.map(|(script_type, script_path)| script_type.as_command(&script_path))
|
||||||
|
.unwrap_or_else(|| Command::new(runnable_name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::env::VarError;
|
use std::env::VarError;
|
||||||
use std::ffi::{OsStr, OsString};
|
use std::ffi::OsString;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
@ -33,6 +33,7 @@ use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
||||||
use uv_resolver::Lock;
|
use uv_resolver::Lock;
|
||||||
use uv_scripts::Pep723Item;
|
use uv_scripts::Pep723Item;
|
||||||
use uv_settings::PythonInstallMirrors;
|
use uv_settings::PythonInstallMirrors;
|
||||||
|
use uv_shell::runnable::WindowsRunnable;
|
||||||
use uv_static::EnvVars;
|
use uv_static::EnvVars;
|
||||||
use uv_warnings::warn_user;
|
use uv_warnings::warn_user;
|
||||||
use uv_workspace::{DiscoveryOptions, VirtualProject, Workspace, WorkspaceCache, WorkspaceError};
|
use uv_workspace::{DiscoveryOptions, VirtualProject, Workspace, WorkspaceCache, WorkspaceError};
|
||||||
|
|
@ -1126,58 +1127,6 @@ fn can_skip_ephemeral(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum WindowsScript {
|
|
||||||
/// `PowerShell` script (.ps1)
|
|
||||||
PowerShell,
|
|
||||||
/// Command Prompt NT script (.cmd)
|
|
||||||
Command,
|
|
||||||
/// Command Prompt script (.bat)
|
|
||||||
Batch,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WindowsScript {
|
|
||||||
/// Returns a list of all supported Windows script types.
|
|
||||||
fn all() -> &'static [Self] {
|
|
||||||
&[Self::PowerShell, Self::Command, Self::Batch]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the script extension for a given Windows script type.
|
|
||||||
fn to_extension(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
Self::PowerShell => "ps1",
|
|
||||||
Self::Command => "cmd",
|
|
||||||
Self::Batch => "bat",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determines the script type from a given Windows file extension.
|
|
||||||
fn from_extension(ext: &str) -> Option<Self> {
|
|
||||||
match ext {
|
|
||||||
"ps1" => Some(Self::PowerShell),
|
|
||||||
"cmd" => Some(Self::Command),
|
|
||||||
"bat" => Some(Self::Batch),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a [`Command`] to run the given script under the appropriate Windows command.
|
|
||||||
fn as_command(&self, script: &Path) -> Command {
|
|
||||||
match self {
|
|
||||||
Self::PowerShell => {
|
|
||||||
let mut cmd = Command::new("powershell");
|
|
||||||
cmd.arg("-NoLogo").arg("-File").arg(script);
|
|
||||||
cmd
|
|
||||||
}
|
|
||||||
Self::Command | Self::Batch => {
|
|
||||||
let mut cmd = Command::new("cmd");
|
|
||||||
cmd.arg("/q").arg("/c").arg(script);
|
|
||||||
cmd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum RunCommand {
|
pub(crate) enum RunCommand {
|
||||||
/// Execute `python`.
|
/// Execute `python`.
|
||||||
|
|
@ -1239,37 +1188,6 @@ impl RunCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle legacy setuptools scripts for Windows.
|
|
||||||
///
|
|
||||||
/// Returns [`Command`] that can be used to run `.ps1`, `.cmd`, or `.bat` scripts on Windows.
|
|
||||||
fn for_windows_script(interpreter: &Interpreter, executable: &OsStr) -> Command {
|
|
||||||
let script_path = interpreter.scripts().join(executable);
|
|
||||||
|
|
||||||
// Honor explicit extension if provided and recognized.
|
|
||||||
if let Some(script_type) = script_path
|
|
||||||
.extension()
|
|
||||||
.and_then(OsStr::to_str)
|
|
||||||
.and_then(WindowsScript::from_extension)
|
|
||||||
.filter(|_| script_path.is_file())
|
|
||||||
{
|
|
||||||
return script_type.as_command(&script_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Guess the extension when an explicit one is not provided.
|
|
||||||
// We also add the extension when missing since for PowerShell it must be explicit.
|
|
||||||
WindowsScript::all()
|
|
||||||
.iter()
|
|
||||||
.map(|script_type| {
|
|
||||||
(
|
|
||||||
script_type,
|
|
||||||
script_path.with_extension(script_type.to_extension()),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.find(|(_, script_path)| script_path.is_file())
|
|
||||||
.map(|(script_type, script_path)| script_type.as_command(&script_path))
|
|
||||||
.unwrap_or_else(|| Command::new(executable))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert a [`RunCommand`] into a [`Command`].
|
/// Convert a [`RunCommand`] into a [`Command`].
|
||||||
fn as_command(&self, interpreter: &Interpreter) -> Command {
|
fn as_command(&self, interpreter: &Interpreter) -> Command {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -1386,7 +1304,7 @@ impl RunCommand {
|
||||||
}
|
}
|
||||||
Self::External(executable, args) => {
|
Self::External(executable, args) => {
|
||||||
let mut process = if cfg!(windows) {
|
let mut process = if cfg!(windows) {
|
||||||
Self::for_windows_script(interpreter, executable)
|
WindowsRunnable::from_script_path(interpreter.scripts(), executable).into()
|
||||||
} else {
|
} else {
|
||||||
Command::new(executable)
|
Command::new(executable)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ use uv_python::{
|
||||||
};
|
};
|
||||||
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
||||||
use uv_settings::{PythonInstallMirrors, ResolverInstallerOptions, ToolOptions};
|
use uv_settings::{PythonInstallMirrors, ResolverInstallerOptions, ToolOptions};
|
||||||
|
use uv_shell::runnable::WindowsRunnable;
|
||||||
use uv_static::EnvVars;
|
use uv_static::EnvVars;
|
||||||
use uv_tool::{entrypoint_paths, InstalledTools};
|
use uv_tool::{entrypoint_paths, InstalledTools};
|
||||||
use uv_warnings::warn_user;
|
use uv_warnings::warn_user;
|
||||||
|
|
@ -260,7 +261,12 @@ pub(crate) async fn run(
|
||||||
let executable = from.executable();
|
let executable = from.executable();
|
||||||
|
|
||||||
// Construct the command
|
// Construct the command
|
||||||
let mut process = Command::new(executable);
|
let mut process = if cfg!(windows) {
|
||||||
|
WindowsRunnable::from_script_path(environment.scripts(), executable.as_ref()).into()
|
||||||
|
} else {
|
||||||
|
Command::new(executable)
|
||||||
|
};
|
||||||
|
|
||||||
process.args(args);
|
process.args(args);
|
||||||
|
|
||||||
// Construct the `PATH` environment variable.
|
// Construct the `PATH` environment variable.
|
||||||
|
|
|
||||||
|
|
@ -4560,7 +4560,7 @@ fn run_windows_legacy_scripts() -> Result<()> {
|
||||||
Audited 1 package in [TIME]
|
Audited 1 package in [TIME]
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
// Test without explicit extension (.ps1 should be used)
|
// Test without explicit extension (.ps1 should be used) as there's no .exe available.
|
||||||
uv_snapshot!(context.filters(), context.run().arg("custom_pydoc"), @r###"
|
uv_snapshot!(context.filters(), context.run().arg("custom_pydoc"), @r###"
|
||||||
success: true
|
success: true
|
||||||
exit_code: 0
|
exit_code: 0
|
||||||
|
|
|
||||||
|
|
@ -2259,3 +2259,323 @@ fn tool_run_with_script_and_from_script() {
|
||||||
hint: If you meant to run a command from the `script-py` package, use the normalized package name instead to disambiguate, e.g., `uv tool run --from script-py other-script.py`
|
hint: If you meant to run a command from the `script-py` package, use the normalized package name instead to disambiguate, e.g., `uv tool run --from script-py other-script.py`
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test windows runnable types, namely console scripts and legacy setuptools scripts.
|
||||||
|
/// Console Scripts <https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#console-scripts>
|
||||||
|
/// Legacy Scripts <https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/#scripts>.
|
||||||
|
///
|
||||||
|
/// This tests for uv tool run of windows runnable types defined by [`WindowsRunnable`].
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[test]
|
||||||
|
fn tool_run_windows_runnable_types() -> anyhow::Result<()> {
|
||||||
|
let context = TestContext::new("3.12").with_filtered_counts();
|
||||||
|
let tool_dir = context.temp_dir.child("tools");
|
||||||
|
let bin_dir = context.temp_dir.child("bin");
|
||||||
|
|
||||||
|
let foo_dir = context.temp_dir.child("foo");
|
||||||
|
let foo_pyproject_toml = foo_dir.child("pyproject.toml");
|
||||||
|
|
||||||
|
// Use `script-files` which enables legacy scripts packaging.
|
||||||
|
foo_pyproject_toml.write_str(indoc! { r#"
|
||||||
|
[project]
|
||||||
|
name = "foo"
|
||||||
|
version = "1.0.0"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
custom_pydoc = "foo.main:run"
|
||||||
|
|
||||||
|
[tool.setuptools]
|
||||||
|
script-files = [
|
||||||
|
"misc/custom_pydoc.bat",
|
||||||
|
"misc/custom_pydoc.cmd",
|
||||||
|
"misc/custom_pydoc.ps1"
|
||||||
|
]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
"#
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Create the legacy scripts
|
||||||
|
let custom_pydoc_bat = foo_dir.child("misc").child("custom_pydoc.bat");
|
||||||
|
let custom_pydoc_cmd = foo_dir.child("misc").child("custom_pydoc.cmd");
|
||||||
|
let custom_pydoc_ps1 = foo_dir.child("misc").child("custom_pydoc.ps1");
|
||||||
|
|
||||||
|
custom_pydoc_bat.write_str("python.exe -m pydoc %*")?;
|
||||||
|
custom_pydoc_cmd.write_str("python.exe -m pydoc %*")?;
|
||||||
|
custom_pydoc_ps1.write_str("python.exe -m pydoc $args")?;
|
||||||
|
|
||||||
|
// Create the foo module
|
||||||
|
let foo_project_src = foo_dir.child("src");
|
||||||
|
let foo_module = foo_project_src.child("foo");
|
||||||
|
let foo_main_py = foo_module.child("main.py");
|
||||||
|
foo_main_py.write_str(indoc! { r#"
|
||||||
|
import pydoc, sys
|
||||||
|
|
||||||
|
def run():
|
||||||
|
sys.argv[0] = "pydoc"
|
||||||
|
pydoc.cli()
|
||||||
|
|
||||||
|
__name__ == "__main__" and run()
|
||||||
|
"#
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Install `foo` tool.
|
||||||
|
context
|
||||||
|
.tool_install()
|
||||||
|
.arg(foo_dir.as_os_str())
|
||||||
|
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||||
|
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str())
|
||||||
|
.env(EnvVars::PATH, bin_dir.as_os_str())
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.tool_run()
|
||||||
|
.arg("--from")
|
||||||
|
.arg("foo")
|
||||||
|
.arg("does_not_exist")
|
||||||
|
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||||
|
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str())
|
||||||
|
.env(EnvVars::PATH, bin_dir.as_os_str()), @r###"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
The executable `does_not_exist` was not found.
|
||||||
|
The following executables are provided by `foo`:
|
||||||
|
- custom_pydoc.exe
|
||||||
|
- custom_pydoc.bat
|
||||||
|
- custom_pydoc.cmd
|
||||||
|
- custom_pydoc.ps1
|
||||||
|
Consider using `uv tool run --from foo <EXECUTABLE_NAME>` instead.
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: An executable named `does_not_exist` is not provided by package `foo`.
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Test with explicit .bat extension
|
||||||
|
uv_snapshot!(context.filters(), context.tool_run()
|
||||||
|
.arg("--from")
|
||||||
|
.arg("foo")
|
||||||
|
.arg("custom_pydoc.bat")
|
||||||
|
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||||
|
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
pydoc - the Python documentation tool
|
||||||
|
|
||||||
|
pydoc <name> ...
|
||||||
|
Show text documentation on something. <name> may be the name of a
|
||||||
|
Python keyword, topic, function, module, or package, or a dotted
|
||||||
|
reference to a class or function within a module or module in a
|
||||||
|
package. If <name> contains a '\', it is used as the path to a
|
||||||
|
Python source file to document. If name is 'keywords', 'topics',
|
||||||
|
or 'modules', a listing of these things is displayed.
|
||||||
|
|
||||||
|
pydoc -k <keyword>
|
||||||
|
Search for a keyword in the synopsis lines of all available modules.
|
||||||
|
|
||||||
|
pydoc -n <hostname>
|
||||||
|
Start an HTTP server with the given hostname (default: localhost).
|
||||||
|
|
||||||
|
pydoc -p <port>
|
||||||
|
Start an HTTP server on the given port on the local machine. Port
|
||||||
|
number 0 can be used to get an arbitrary unused port.
|
||||||
|
|
||||||
|
pydoc -b
|
||||||
|
Start an HTTP server on an arbitrary unused port and open a web browser
|
||||||
|
to interactively browse documentation. This option can be used in
|
||||||
|
combination with -n and/or -p.
|
||||||
|
|
||||||
|
pydoc -w <name> ...
|
||||||
|
Write out the HTML documentation for a module to a file in the current
|
||||||
|
directory. If <name> contains a '\', it is treated as a filename; if
|
||||||
|
it names a directory, documentation is written for all the contents.
|
||||||
|
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Test with explicit .cmd extension
|
||||||
|
uv_snapshot!(context.filters(), context.tool_run()
|
||||||
|
.arg("--from")
|
||||||
|
.arg("foo")
|
||||||
|
.arg("custom_pydoc.cmd")
|
||||||
|
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||||
|
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
pydoc - the Python documentation tool
|
||||||
|
|
||||||
|
pydoc <name> ...
|
||||||
|
Show text documentation on something. <name> may be the name of a
|
||||||
|
Python keyword, topic, function, module, or package, or a dotted
|
||||||
|
reference to a class or function within a module or module in a
|
||||||
|
package. If <name> contains a '\', it is used as the path to a
|
||||||
|
Python source file to document. If name is 'keywords', 'topics',
|
||||||
|
or 'modules', a listing of these things is displayed.
|
||||||
|
|
||||||
|
pydoc -k <keyword>
|
||||||
|
Search for a keyword in the synopsis lines of all available modules.
|
||||||
|
|
||||||
|
pydoc -n <hostname>
|
||||||
|
Start an HTTP server with the given hostname (default: localhost).
|
||||||
|
|
||||||
|
pydoc -p <port>
|
||||||
|
Start an HTTP server on the given port on the local machine. Port
|
||||||
|
number 0 can be used to get an arbitrary unused port.
|
||||||
|
|
||||||
|
pydoc -b
|
||||||
|
Start an HTTP server on an arbitrary unused port and open a web browser
|
||||||
|
to interactively browse documentation. This option can be used in
|
||||||
|
combination with -n and/or -p.
|
||||||
|
|
||||||
|
pydoc -w <name> ...
|
||||||
|
Write out the HTML documentation for a module to a file in the current
|
||||||
|
directory. If <name> contains a '\', it is treated as a filename; if
|
||||||
|
it names a directory, documentation is written for all the contents.
|
||||||
|
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Test with explicit .ps1 extension
|
||||||
|
uv_snapshot!(context.filters(), context.tool_run()
|
||||||
|
.arg("--from")
|
||||||
|
.arg("foo")
|
||||||
|
.arg("custom_pydoc.ps1")
|
||||||
|
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||||
|
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str()), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
pydoc - the Python documentation tool
|
||||||
|
|
||||||
|
pydoc <name> ...
|
||||||
|
Show text documentation on something. <name> may be the name of a
|
||||||
|
Python keyword, topic, function, module, or package, or a dotted
|
||||||
|
reference to a class or function within a module or module in a
|
||||||
|
package. If <name> contains a '\', it is used as the path to a
|
||||||
|
Python source file to document. If name is 'keywords', 'topics',
|
||||||
|
or 'modules', a listing of these things is displayed.
|
||||||
|
|
||||||
|
pydoc -k <keyword>
|
||||||
|
Search for a keyword in the synopsis lines of all available modules.
|
||||||
|
|
||||||
|
pydoc -n <hostname>
|
||||||
|
Start an HTTP server with the given hostname (default: localhost).
|
||||||
|
|
||||||
|
pydoc -p <port>
|
||||||
|
Start an HTTP server on the given port on the local machine. Port
|
||||||
|
number 0 can be used to get an arbitrary unused port.
|
||||||
|
|
||||||
|
pydoc -b
|
||||||
|
Start an HTTP server on an arbitrary unused port and open a web browser
|
||||||
|
to interactively browse documentation. This option can be used in
|
||||||
|
combination with -n and/or -p.
|
||||||
|
|
||||||
|
pydoc -w <name> ...
|
||||||
|
Write out the HTML documentation for a module to a file in the current
|
||||||
|
directory. If <name> contains a '\', it is treated as a filename; if
|
||||||
|
it names a directory, documentation is written for all the contents.
|
||||||
|
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Test with explicit .exe extension
|
||||||
|
uv_snapshot!(context.filters(), context.tool_run()
|
||||||
|
.arg("--from")
|
||||||
|
.arg("foo")
|
||||||
|
.arg("custom_pydoc")
|
||||||
|
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||||
|
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str())
|
||||||
|
.env(EnvVars::PATH, bin_dir.as_os_str()), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
pydoc - the Python documentation tool
|
||||||
|
|
||||||
|
pydoc <name> ...
|
||||||
|
Show text documentation on something. <name> may be the name of a
|
||||||
|
Python keyword, topic, function, module, or package, or a dotted
|
||||||
|
reference to a class or function within a module or module in a
|
||||||
|
package. If <name> contains a '\', it is used as the path to a
|
||||||
|
Python source file to document. If name is 'keywords', 'topics',
|
||||||
|
or 'modules', a listing of these things is displayed.
|
||||||
|
|
||||||
|
pydoc -k <keyword>
|
||||||
|
Search for a keyword in the synopsis lines of all available modules.
|
||||||
|
|
||||||
|
pydoc -n <hostname>
|
||||||
|
Start an HTTP server with the given hostname (default: localhost).
|
||||||
|
|
||||||
|
pydoc -p <port>
|
||||||
|
Start an HTTP server on the given port on the local machine. Port
|
||||||
|
number 0 can be used to get an arbitrary unused port.
|
||||||
|
|
||||||
|
pydoc -b
|
||||||
|
Start an HTTP server on an arbitrary unused port and open a web browser
|
||||||
|
to interactively browse documentation. This option can be used in
|
||||||
|
combination with -n and/or -p.
|
||||||
|
|
||||||
|
pydoc -w <name> ...
|
||||||
|
Write out the HTML documentation for a module to a file in the current
|
||||||
|
directory. If <name> contains a '\', it is treated as a filename; if
|
||||||
|
it names a directory, documentation is written for all the contents.
|
||||||
|
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Test without explicit extension (.exe should be used)
|
||||||
|
uv_snapshot!(context.filters(), context.tool_run()
|
||||||
|
.arg("--from")
|
||||||
|
.arg("foo")
|
||||||
|
.arg("custom_pydoc")
|
||||||
|
.env(EnvVars::UV_TOOL_DIR, tool_dir.as_os_str())
|
||||||
|
.env(EnvVars::XDG_BIN_HOME, bin_dir.as_os_str())
|
||||||
|
.env(EnvVars::PATH, bin_dir.as_os_str()), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
pydoc - the Python documentation tool
|
||||||
|
|
||||||
|
pydoc <name> ...
|
||||||
|
Show text documentation on something. <name> may be the name of a
|
||||||
|
Python keyword, topic, function, module, or package, or a dotted
|
||||||
|
reference to a class or function within a module or module in a
|
||||||
|
package. If <name> contains a '\', it is used as the path to a
|
||||||
|
Python source file to document. If name is 'keywords', 'topics',
|
||||||
|
or 'modules', a listing of these things is displayed.
|
||||||
|
|
||||||
|
pydoc -k <keyword>
|
||||||
|
Search for a keyword in the synopsis lines of all available modules.
|
||||||
|
|
||||||
|
pydoc -n <hostname>
|
||||||
|
Start an HTTP server with the given hostname (default: localhost).
|
||||||
|
|
||||||
|
pydoc -p <port>
|
||||||
|
Start an HTTP server on the given port on the local machine. Port
|
||||||
|
number 0 can be used to get an arbitrary unused port.
|
||||||
|
|
||||||
|
pydoc -b
|
||||||
|
Start an HTTP server on an arbitrary unused port and open a web browser
|
||||||
|
to interactively browse documentation. This option can be used in
|
||||||
|
combination with -n and/or -p.
|
||||||
|
|
||||||
|
pydoc -w <name> ...
|
||||||
|
Write out the HTML documentation for a module to a file in the current
|
||||||
|
directory. If <name> contains a '\', it is treated as a filename; if
|
||||||
|
it names a directory, documentation is written for all the contents.
|
||||||
|
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,27 @@ print([(k, v["title"]) for k, v in data.items()][:10])
|
||||||
The invocation `uv run example.py` would run _isolated_ from the project with only the given
|
The invocation `uv run example.py` would run _isolated_ from the project with only the given
|
||||||
dependencies listed.
|
dependencies listed.
|
||||||
|
|
||||||
|
## Legacy Windows Scripts
|
||||||
|
|
||||||
|
Support is provided for
|
||||||
|
[legacy setuptools scripts](https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/#scripts).
|
||||||
|
These types of scripts are additional files installed by setuptools in `.venv\Scripts`.
|
||||||
|
|
||||||
|
Currently only legacy scripts with the `.ps1`, `.cmd`, and `.bat` extensions are supported.
|
||||||
|
|
||||||
|
For example, below is an example running a Command Prompt script.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ uv run --with nuitka==2.6.7 -- nuitka.cmd --version
|
||||||
|
```
|
||||||
|
|
||||||
|
In addition, you don't need to specify the extension. `uv` will automatically look for files ending
|
||||||
|
in `.ps1`, `.cmd`, and `.bat` in that order of execution on your behalf.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ uv run --with nuitka==2.6.7 -- nuitka --version
|
||||||
|
```
|
||||||
|
|
||||||
## Signal handling
|
## Signal handling
|
||||||
|
|
||||||
uv does not cede control of the process to the spawned command in order to provide better error
|
uv does not cede control of the process to the spawned command in order to provide better error
|
||||||
|
|
|
||||||
|
|
@ -264,6 +264,27 @@ $ uv tool upgrade --python 3.10 ruff
|
||||||
For more details on requesting Python versions, see the
|
For more details on requesting Python versions, see the
|
||||||
[Python version](../concepts/python-versions.md#requesting-a-version) concept page..
|
[Python version](../concepts/python-versions.md#requesting-a-version) concept page..
|
||||||
|
|
||||||
|
## Legacy Windows Scripts
|
||||||
|
|
||||||
|
Tools also support running
|
||||||
|
[legacy setuptools scripts](https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/#scripts).
|
||||||
|
These scripts are available via `$(uv tool dir)\<tool-name>\Scripts` when installed.
|
||||||
|
|
||||||
|
Currently only legacy scripts with the `.ps1`, `.cmd`, and `.bat` extensions are supported.
|
||||||
|
|
||||||
|
For example, below is an example running a Command Prompt script.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ uv tool run --from nuitka==2.6.7 nuitka.cmd --version
|
||||||
|
```
|
||||||
|
|
||||||
|
In addition, you don't need to specify the extension. `uvx` will automatically look for files ending
|
||||||
|
in `.ps1`, `.cmd`, and `.bat` in that order of execution on your behalf.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ uv tool run --from nuitka==2.6.7 nuitka --version
|
||||||
|
```
|
||||||
|
|
||||||
## Next steps
|
## Next steps
|
||||||
|
|
||||||
To learn more about managing tools with uv, see the [Tools concept](../concepts/tools.md) page and
|
To learn more about managing tools with uv, see the [Tools concept](../concepts/tools.md) page and
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue