mirror of https://github.com/astral-sh/uv
Support `uv run -m foo` to run a module (#7754)
## Summary This is another attempt using `module: bool` instead of `module: Option<String>` following #7322. The original PR can't be reopened after a force-push to the branch, I've created this new PR. Resolves #6638
This commit is contained in:
parent
1cae78dd03
commit
0dbf9ae4a7
|
|
@ -2469,6 +2469,12 @@ pub struct RunArgs {
|
||||||
#[arg(long, overrides_with("dev"))]
|
#[arg(long, overrides_with("dev"))]
|
||||||
pub no_dev: bool,
|
pub no_dev: bool,
|
||||||
|
|
||||||
|
/// Run a Python module.
|
||||||
|
///
|
||||||
|
/// Equivalent to `python -m <module>`.
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub module: bool,
|
||||||
|
|
||||||
/// Omit non-development dependencies.
|
/// Omit non-development dependencies.
|
||||||
///
|
///
|
||||||
/// The project itself will also be omitted.
|
/// The project itself will also be omitted.
|
||||||
|
|
@ -2495,7 +2501,7 @@ pub struct RunArgs {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub with: Vec<String>,
|
pub with: Vec<String>,
|
||||||
|
|
||||||
/// Run with the given packages installed as editables
|
/// Run with the given packages installed as editables.
|
||||||
///
|
///
|
||||||
/// When used in a project, these dependencies will be layered on top of
|
/// When used in a project, these dependencies will be layered on top of
|
||||||
/// the project environment in a separate, ephemeral environment. These
|
/// the project environment in a separate, ephemeral environment. These
|
||||||
|
|
|
||||||
|
|
@ -832,6 +832,9 @@ pub(crate) enum RunCommand {
|
||||||
Python(Vec<OsString>),
|
Python(Vec<OsString>),
|
||||||
/// Execute a `python` script.
|
/// Execute a `python` script.
|
||||||
PythonScript(PathBuf, Vec<OsString>),
|
PythonScript(PathBuf, Vec<OsString>),
|
||||||
|
/// Search `sys.path` for the named module and execute its contents as the `__main__` module.
|
||||||
|
/// Equivalent to `python -m module`.
|
||||||
|
PythonModule(OsString, Vec<OsString>),
|
||||||
/// Execute a `pythonw` script (Windows only).
|
/// Execute a `pythonw` script (Windows only).
|
||||||
PythonGuiScript(PathBuf, Vec<OsString>),
|
PythonGuiScript(PathBuf, Vec<OsString>),
|
||||||
/// Execute a Python package containing a `__main__.py` file.
|
/// Execute a Python package containing a `__main__.py` file.
|
||||||
|
|
@ -856,6 +859,7 @@ impl RunCommand {
|
||||||
| Self::PythonPackage(..)
|
| Self::PythonPackage(..)
|
||||||
| Self::PythonZipapp(..)
|
| Self::PythonZipapp(..)
|
||||||
| Self::Empty => Cow::Borrowed("python"),
|
| Self::Empty => Cow::Borrowed("python"),
|
||||||
|
Self::PythonModule(..) => Cow::Borrowed("python -m"),
|
||||||
Self::PythonGuiScript(..) => Cow::Borrowed("pythonw"),
|
Self::PythonGuiScript(..) => Cow::Borrowed("pythonw"),
|
||||||
Self::PythonStdin(_) => Cow::Borrowed("python -c"),
|
Self::PythonStdin(_) => Cow::Borrowed("python -c"),
|
||||||
Self::External(executable, _) => executable.to_string_lossy(),
|
Self::External(executable, _) => executable.to_string_lossy(),
|
||||||
|
|
@ -878,6 +882,13 @@ impl RunCommand {
|
||||||
process.args(args);
|
process.args(args);
|
||||||
process
|
process
|
||||||
}
|
}
|
||||||
|
Self::PythonModule(module, args) => {
|
||||||
|
let mut process = Command::new(interpreter.sys_executable());
|
||||||
|
process.arg("-m");
|
||||||
|
process.arg(module);
|
||||||
|
process.args(args);
|
||||||
|
process
|
||||||
|
}
|
||||||
Self::PythonGuiScript(target, args) => {
|
Self::PythonGuiScript(target, args) => {
|
||||||
let python_executable = interpreter.sys_executable();
|
let python_executable = interpreter.sys_executable();
|
||||||
|
|
||||||
|
|
@ -944,6 +955,14 @@ impl std::fmt::Display for RunCommand {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Self::PythonModule(module, args) => {
|
||||||
|
write!(f, "python -m")?;
|
||||||
|
write!(f, " {}", module.to_string_lossy())?;
|
||||||
|
for arg in args {
|
||||||
|
write!(f, " {}", arg.to_string_lossy())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Self::PythonGuiScript(target, args) => {
|
Self::PythonGuiScript(target, args) => {
|
||||||
write!(f, "pythonw {}", target.display())?;
|
write!(f, "pythonw {}", target.display())?;
|
||||||
for arg in args {
|
for arg in args {
|
||||||
|
|
@ -970,17 +989,18 @@ impl std::fmt::Display for RunCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&ExternalCommand> for RunCommand {
|
impl RunCommand {
|
||||||
type Error = std::io::Error;
|
pub(crate) fn from_args(command: &ExternalCommand, module: bool) -> anyhow::Result<Self> {
|
||||||
|
|
||||||
fn try_from(command: &ExternalCommand) -> Result<Self, Self::Error> {
|
|
||||||
let (target, args) = command.split();
|
let (target, args) = command.split();
|
||||||
|
|
||||||
let Some(target) = target else {
|
let Some(target) = target else {
|
||||||
return Ok(Self::Empty);
|
return Ok(Self::Empty);
|
||||||
};
|
};
|
||||||
|
|
||||||
let target_path = PathBuf::from(&target);
|
if module {
|
||||||
|
return Ok(Self::PythonModule(target.clone(), args.to_vec()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let target_path = PathBuf::from(target);
|
||||||
let metadata = target_path.metadata();
|
let metadata = target_path.metadata();
|
||||||
let is_file = metadata.as_ref().map_or(false, std::fs::Metadata::is_file);
|
let is_file = metadata.as_ref().map_or(false, std::fs::Metadata::is_file);
|
||||||
let is_dir = metadata.as_ref().map_or(false, std::fs::Metadata::is_dir);
|
let is_dir = metadata.as_ref().map_or(false, std::fs::Metadata::is_dir);
|
||||||
|
|
|
||||||
|
|
@ -131,8 +131,11 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
||||||
|
|
||||||
// Parse the external command, if necessary.
|
// Parse the external command, if necessary.
|
||||||
let run_command = if let Commands::Project(command) = &*cli.command {
|
let run_command = if let Commands::Project(command) = &*cli.command {
|
||||||
if let ProjectCommand::Run(uv_cli::RunArgs { command, .. }) = &**command {
|
if let ProjectCommand::Run(uv_cli::RunArgs {
|
||||||
Some(RunCommand::try_from(command)?)
|
command, module, ..
|
||||||
|
}) = &**command
|
||||||
|
{
|
||||||
|
Some(RunCommand::from_args(command, *module)?)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,7 @@ impl RunSettings {
|
||||||
no_all_extras,
|
no_all_extras,
|
||||||
dev,
|
dev,
|
||||||
no_dev,
|
no_dev,
|
||||||
|
module: _,
|
||||||
only_dev,
|
only_dev,
|
||||||
no_editable,
|
no_editable,
|
||||||
command: _,
|
command: _,
|
||||||
|
|
|
||||||
|
|
@ -1830,6 +1830,43 @@ fn run_zipapp() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Run a module equivalent to `python -m foo`.
|
||||||
|
#[test]
|
||||||
|
fn run_module() {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("-m").arg("__hello__"), @r#"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
Hello world!
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"#);
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("-m").arg("http.server").arg("-h"), @r#"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
usage: server.py [-h] [--cgi] [-b ADDRESS] [-d DIRECTORY] [-p VERSION] [port]
|
||||||
|
|
||||||
|
positional arguments:
|
||||||
|
port bind to this port (default: 8000)
|
||||||
|
|
||||||
|
options:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
--cgi run as CGI server
|
||||||
|
-b ADDRESS, --bind ADDRESS
|
||||||
|
bind to this address (default: all interfaces)
|
||||||
|
-d DIRECTORY, --directory DIRECTORY
|
||||||
|
serve this directory (default: current directory)
|
||||||
|
-p VERSION, --protocol VERSION
|
||||||
|
conform to this HTTP version (default: HTTP/1.0)
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"#);
|
||||||
|
}
|
||||||
|
|
||||||
/// When the `pyproject.toml` file is invalid.
|
/// When the `pyproject.toml` file is invalid.
|
||||||
#[test]
|
#[test]
|
||||||
fn run_project_toml_error() -> Result<()> {
|
fn run_project_toml_error() -> Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -217,6 +217,10 @@ uv run [OPTIONS] <COMMAND>
|
||||||
|
|
||||||
<p>Requires that the lockfile is up-to-date. If the lockfile is missing or needs to be updated, uv will exit with an error.</p>
|
<p>Requires that the lockfile is up-to-date. If the lockfile is missing or needs to be updated, uv will exit with an error.</p>
|
||||||
|
|
||||||
|
</dd><dt><code>--module</code>, <code>-m</code></dt><dd><p>Run a Python module.</p>
|
||||||
|
|
||||||
|
<p>Equivalent to <code>python -m <module></code>.</p>
|
||||||
|
|
||||||
</dd><dt><code>--native-tls</code></dt><dd><p>Whether to load TLS certificates from the platform’s native certificate store.</p>
|
</dd><dt><code>--native-tls</code></dt><dd><p>Whether to load TLS certificates from the platform’s native certificate store.</p>
|
||||||
|
|
||||||
<p>By default, uv loads certificates from the bundled <code>webpki-roots</code> crate. The <code>webpki-roots</code> are a reliable set of trust roots from Mozilla, and including them in uv improves portability and performance (especially on macOS).</p>
|
<p>By default, uv loads certificates from the bundled <code>webpki-roots</code> crate. The <code>webpki-roots</code> are a reliable set of trust roots from Mozilla, and including them in uv improves portability and performance (especially on macOS).</p>
|
||||||
|
|
@ -380,7 +384,7 @@ uv run [OPTIONS] <COMMAND>
|
||||||
|
|
||||||
<p>When used in a project, these dependencies will be layered on top of the project environment in a separate, ephemeral environment. These dependencies are allowed to conflict with those specified by the project.</p>
|
<p>When used in a project, these dependencies will be layered on top of the project environment in a separate, ephemeral environment. These dependencies are allowed to conflict with those specified by the project.</p>
|
||||||
|
|
||||||
</dd><dt><code>--with-editable</code> <i>with-editable</i></dt><dd><p>Run with the given packages installed as editables</p>
|
</dd><dt><code>--with-editable</code> <i>with-editable</i></dt><dd><p>Run with the given packages installed as editables.</p>
|
||||||
|
|
||||||
<p>When used in a project, these dependencies will be layered on top of the project environment in a separate, ephemeral environment. These dependencies are allowed to conflict with those specified by the project.</p>
|
<p>When used in a project, these dependencies will be layered on top of the project environment in a separate, ephemeral environment. These dependencies are allowed to conflict with those specified by the project.</p>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue