Skip to content

Commit

Permalink
support pdm or pyproject like scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
light4 committed Jun 22, 2023
1 parent 0bde3ba commit a8fc40a
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 18 deletions.
9 changes: 9 additions & 0 deletions docs/guide/pyproject.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,15 @@ lint = { chain = ["lint:black", "lint:flake8" ] }
"lint:flake8" = "flake8 src"
```

### `call`

This is a special key that can be set instead of `cmd` to make a command callable without install.

```toml
[tool.rye.scripts]
entry = { call = "hello.world:main" }
```

## `tool.rye.workspace`

When a table with that key is stored, then a project is declared to be a workspace root. By
Expand Down
25 changes: 25 additions & 0 deletions rye/src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,31 @@ fn invoke_script(
let mut env_overrides = None;

match pyproject.get_script_cmd(&args[0].to_string_lossy()) {
Some(Script::Call(entry, env_vars)) => {
if entry.is_empty() {
bail!("script has no arguments");
}
env_overrides = Some(env_vars);
let splited: Vec<&str> = entry.split(':').collect();
let (module, func) = (splited[0], splited[1]);
if module.is_empty() || func.is_empty() {
bail!("Python callable must be in the form <module_name>:<callable_name>");
}
let short_name = "_1";
let real_func = if regex::Regex::new(r"\(.*?\)").unwrap().find(func).is_none() {
format!("{func}()")
} else {
func.to_string()
};
args = [
"python",
"-c",
&format!("import sys, {module} as {short_name};sys.exit({short_name}.{real_func})"),
]
.into_iter()
.map(OsString::from)
.collect();
}
Some(Script::Cmd(script_args, env_vars)) => {
if script_args.is_empty() {
bail!("script has no arguments");
Expand Down
59 changes: 41 additions & 18 deletions rye/src/pyproject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use pep440_rs::{Operator, Version, VersionSpecifiers};
use pep508_rs::Requirement;
use regex::Regex;
use serde::Serialize;
use toml_edit::{Array, Document, Formatted, Item, Table, Value};
use toml_edit::{Array, Document, Formatted, Item, Table, TableLike, Value};
use url::Url;

use crate::config::Config;
Expand Down Expand Up @@ -182,6 +182,8 @@ type EnvVars = HashMap<String, String>;
/// A reference to a script
#[derive(Clone, Debug)]
pub enum Script {
/// Call python module entry
Call(String, EnvVars),
/// A command alias
Cmd(Vec<String>, EnvVars),
/// A multi-script execution
Expand Down Expand Up @@ -210,29 +212,38 @@ fn toml_value_as_command_args(value: &Value) -> Option<Vec<String>> {

impl Script {
fn from_toml_item(item: &Item) -> Option<Script> {
fn get_env_vars(detailed: &dyn TableLike) -> HashMap<String, String> {
let env_vars = detailed
.get("env")
.and_then(|x| x.as_table_like())
.map(|x| {
x.iter()
.map(|x| {
(
x.0.to_string(),
x.1.as_str()
.map(|x| x.to_string())
.unwrap_or_else(|| x.1.to_string()),
)
})
.collect()
})
.unwrap_or_default();
env_vars
}

if let Some(detailed) = item.as_table_like() {
if let Some(cmds) = detailed.get("chain").and_then(|x| x.as_array()) {
if let Some(call) = detailed.get("call") {
let entry = call.as_str()?.to_string();
let env_vars = get_env_vars(detailed);
Some(Script::Call(entry, env_vars))
} else if let Some(cmds) = detailed.get("chain").and_then(|x| x.as_array()) {
Some(Script::Chain(
cmds.iter().flat_map(toml_value_as_command_args).collect(),
))
} else if let Some(cmd) = detailed.get("cmd") {
let cmd = toml_value_as_command_args(cmd.as_value()?)?;
let env_vars = detailed
.get("env")
.and_then(|x| x.as_table_like())
.map(|x| {
x.iter()
.map(|x| {
(
x.0.to_string(),
x.1.as_str()
.map(|x| x.to_string())
.unwrap_or_else(|| x.1.to_string()),
)
})
.collect()
})
.unwrap_or_default();
let env_vars = get_env_vars(detailed);
Some(Script::Cmd(cmd, env_vars))
} else {
None
Expand All @@ -247,6 +258,18 @@ impl Script {
impl fmt::Display for Script {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Script::Call(entry, env) => {
let mut need_space = false;
for (key, value) in env.iter() {
if need_space {
write!(f, " ")?;
}
write!(f, "{}={}", shlex::quote(key), shlex::quote(value))?;
need_space = true;
}
write!(f, "{}", shlex::quote(entry))?;
Ok(())
}
Script::Cmd(args, env) => {
let mut need_space = false;
for (key, value) in env.iter() {
Expand Down

0 comments on commit a8fc40a

Please sign in to comment.