Support dotted function paths for script entrypoints (#1622)

Co-authored-by: markm <mark.mcmahon@autodesk.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
markmmm 2024-02-19 18:09:12 +08:00 committed by GitHub
parent 4dfcf32e4c
commit b76efc62a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 32 additions and 10 deletions

View file

@ -1,3 +1,4 @@
use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use serde::Serialize; use serde::Serialize;
@ -43,9 +44,11 @@ impl Script {
// between the object reference and the left square bracket, between the extra names and the square brackets and colons delimiting them, // between the object reference and the left square bracket, between the extra names and the square brackets and colons delimiting them,
// and after the right square bracket." // and after the right square bracket."
// https://packaging.python.org/en/latest/specifications/entry-points/#file-format // https://packaging.python.org/en/latest/specifications/entry-points/#file-format
let script_regex = Regex::new(r"^(?P<module>[\w\d_\-.]+)\s*:\s*(?P<function>[\w\d_\-.]+)(?:\s*\[\s*(?P<extras>(?:[^,]+,?\s*)+)\])?\s*$").unwrap(); static SCRIPT_REGEX: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"^(?P<module>[\w\d_\-.]+)\s*:\s*(?P<function>[\w\d_\-.]+)(?:\s*\[\s*(?P<extras>(?:[^,]+,?\s*)+)\])?\s*$").unwrap()
});
let captures = script_regex let captures = SCRIPT_REGEX
.captures(value) .captures(value)
.ok_or_else(|| Error::InvalidWheel(format!("invalid console script: '{value}'")))?; .ok_or_else(|| Error::InvalidWheel(format!("invalid console script: '{value}'")))?;
if let Some(script_extras) = captures.name("extras") { if let Some(script_extras) = captures.name("extras") {
@ -67,6 +70,12 @@ impl Script {
function: captures.name("function").unwrap().as_str().to_string(), function: captures.name("function").unwrap().as_str().to_string(),
})) }))
} }
pub fn import_name(&self) -> &str {
self.function
.split_once('.')
.map_or(&self.function, |(import_name, _)| import_name)
}
} }
#[cfg(test)] #[cfg(test)]
@ -98,4 +107,15 @@ mod test {
); );
} }
} }
#[test]
fn test_split_of_import_name_from_function() {
let entrypoint = "foomod:mod_bar.sub_foo.func_baz";
let script = Script::from_value("script", entrypoint, None)
.unwrap()
.unwrap();
assert_eq!(script.function, "mod_bar.sub_foo.func_baz");
assert_eq!(script.import_name(), "mod_bar");
}
} }

View file

@ -52,7 +52,13 @@ const LAUNCHER_AARCH64_CONSOLE: &[u8] =
/// Wrapper script template function /// Wrapper script template function
/// ///
/// <https://github.com/pypa/pip/blob/7f8a6844037fb7255cfd0d34ff8e8cf44f2598d4/src/pip/_vendor/distlib/scripts.py#L41-L48> /// <https://github.com/pypa/pip/blob/7f8a6844037fb7255cfd0d34ff8e8cf44f2598d4/src/pip/_vendor/distlib/scripts.py#L41-L48>
fn get_script_launcher(module: &str, import_name: &str, shebang: &str) -> String { fn get_script_launcher(entry_point: &Script, shebang: &str) -> String {
let Script {
module, function, ..
} = entry_point;
let import_name = entry_point.import_name();
format!( format!(
r##"{shebang} r##"{shebang}
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
@ -61,7 +67,7 @@ import sys
from {module} import {import_name} from {module} import {import_name}
if __name__ == "__main__": if __name__ == "__main__":
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0]) sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
sys.exit({import_name}()) sys.exit({function}())
"## "##
) )
} }
@ -380,11 +386,7 @@ pub(crate) fn write_script_entrypoints(
}; };
// Generate the launcher script. // Generate the launcher script.
let launcher_python_script = get_script_launcher( let launcher_python_script = get_script_launcher(entrypoint, &get_shebang(location));
&entrypoint.module,
&entrypoint.function,
&get_shebang(location),
);
// If necessary, wrap the launcher script in a Windows launcher binary. // If necessary, wrap the launcher script in a Windows launcher binary.
if cfg!(windows) { if cfg!(windows) {

View file

@ -2682,7 +2682,7 @@ fn repeat_requirement() -> Result<()> {
Resolved 1 package in [TIME] Resolved 1 package in [TIME]
Downloaded 1 package in [TIME] Downloaded 1 package in [TIME]
Installed 1 package in [TIME] Installed 1 package in [TIME]
+ anyio==4.2.0 + anyio==4.3.0
"###); "###);
Ok(()) Ok(())