mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 16:02:19 +00:00
fix: use exact matching for python requirements directive parsing (#8199)
* fix: use exact matching for python requirements directive parsing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: apply same exact matching fix to CLI parser Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -872,16 +872,29 @@ impl WorkspaceDependenciesAnnotatedRefs<String> {
|
||||
validity_re_o: Option<&Regex>,
|
||||
runnable_path: &str,
|
||||
) -> Option<Self> {
|
||||
let (extra_deps, manual_deps) = (format!("extra_{keyword}:"), format!("{keyword}:"));
|
||||
let extra_deps_underscore = format!("extra_{keyword}:");
|
||||
let extra_deps_hyphen = format!("extra-{keyword}:");
|
||||
let manual_deps = format!("{keyword}:");
|
||||
|
||||
let Some((pos, mat)) = code.lines().find_position(|l| {
|
||||
l.starts_with(&comment) && (l.contains(&extra_deps) || l.contains(&manual_deps))
|
||||
}) else {
|
||||
let is_extra = |l: &str| {
|
||||
l.strip_prefix(comment)
|
||||
.map(str::trim_start)
|
||||
.is_some_and(|s| {
|
||||
s.starts_with(&extra_deps_underscore) || s.starts_with(&extra_deps_hyphen)
|
||||
})
|
||||
};
|
||||
let is_manual = |l: &str| {
|
||||
l.strip_prefix(comment)
|
||||
.map(str::trim_start)
|
||||
.is_some_and(|s| s.starts_with(&manual_deps))
|
||||
};
|
||||
|
||||
let Some((pos, mat)) = code.lines().find_position(|l| is_extra(l) || is_manual(l)) else {
|
||||
return None;
|
||||
};
|
||||
let mut lines_it = code.lines().skip(pos);
|
||||
|
||||
let mode = if mat.contains(&extra_deps) {
|
||||
let mode = if is_extra(mat) {
|
||||
Mode::extra
|
||||
} else {
|
||||
Mode::manual
|
||||
@@ -904,7 +917,9 @@ impl WorkspaceDependenciesAnnotatedRefs<String> {
|
||||
.map(|s| {
|
||||
match mode {
|
||||
Mode::manual => s.replace(&manual_deps, ""),
|
||||
Mode::extra => s.replace(&extra_deps, ""),
|
||||
Mode::extra => s
|
||||
.replace(&extra_deps_underscore, "")
|
||||
.replace(&extra_deps_hyphen, ""),
|
||||
}
|
||||
.replace(comment, "")
|
||||
})
|
||||
@@ -993,6 +1008,29 @@ def main():
|
||||
# extra_requirements: utils
|
||||
#numpy>=1.24.0
|
||||
|
||||
def main():
|
||||
pass
|
||||
"#;
|
||||
|
||||
let result = WorkspaceDependenciesAnnotatedRefs::<String>::parse(
|
||||
"#",
|
||||
"requirements",
|
||||
code,
|
||||
None,
|
||||
"",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(matches!(result.mode, Mode::extra));
|
||||
assert_eq!(result.external, vec!["utils".to_owned()]);
|
||||
assert_eq!(result.inline.as_ref().unwrap(), "numpy>=1.24.0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_annotation_python_extra_requirements_hyphen() {
|
||||
let code = r#"
|
||||
# extra-requirements: utils
|
||||
#numpy>=1.24.0
|
||||
|
||||
def main():
|
||||
pass
|
||||
"#;
|
||||
|
||||
@@ -339,16 +339,29 @@ export function extractWorkspaceDepsAnnotation(
|
||||
if (!config) return null;
|
||||
|
||||
const { comment, keyword, validityRe } = config;
|
||||
const extraMarker = `extra_${keyword}:`;
|
||||
const extraMarkerUnderscore = `extra_${keyword}:`;
|
||||
const extraMarkerHyphen = `extra-${keyword}:`;
|
||||
const manualMarker = `${keyword}:`;
|
||||
|
||||
const stripComment = (l: string): string | null => {
|
||||
if (!l.startsWith(comment)) return null;
|
||||
return l.substring(comment.length).trimStart();
|
||||
};
|
||||
const isExtra = (l: string): boolean => {
|
||||
const s = stripComment(l);
|
||||
return s !== null && (s.startsWith(extraMarkerUnderscore) || s.startsWith(extraMarkerHyphen));
|
||||
};
|
||||
const isManual = (l: string): boolean => {
|
||||
const s = stripComment(l);
|
||||
return s !== null && s.startsWith(manualMarker);
|
||||
};
|
||||
|
||||
const lines = scriptContent.split("\n");
|
||||
|
||||
// Find first annotation line (mirrors Rust find_position)
|
||||
let pos = -1;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const l = lines[i];
|
||||
if (l.startsWith(comment) && (l.includes(extraMarker) || l.includes(manualMarker))) {
|
||||
if (isExtra(lines[i]) || isManual(lines[i])) {
|
||||
pos = i;
|
||||
break;
|
||||
}
|
||||
@@ -356,10 +369,12 @@ export function extractWorkspaceDepsAnnotation(
|
||||
if (pos === -1) return null;
|
||||
|
||||
const annotationLine = lines[pos];
|
||||
const mode: AnnotationMode = annotationLine.includes(extraMarker) ? "extra" : "manual";
|
||||
const mode: AnnotationMode = isExtra(annotationLine) ? "extra" : "manual";
|
||||
|
||||
// Parse external references from the annotation line
|
||||
const marker = mode === "extra" ? extraMarker : manualMarker;
|
||||
const marker = mode === "extra"
|
||||
? (annotationLine.includes(extraMarkerUnderscore) ? extraMarkerUnderscore : extraMarkerHyphen)
|
||||
: manualMarker;
|
||||
const unparsed = annotationLine.replaceAll(marker, "").replaceAll(comment, "");
|
||||
const external = unparsed
|
||||
.split(",")
|
||||
|
||||
@@ -46,15 +46,28 @@ function extractWorkspaceDepsAnnotation(
|
||||
if (!config) return null;
|
||||
|
||||
const { comment, keyword, validityRe } = config;
|
||||
const extraMarker = `extra_${keyword}:`;
|
||||
const extraMarkerUnderscore = `extra_${keyword}:`;
|
||||
const extraMarkerHyphen = `extra-${keyword}:`;
|
||||
const manualMarker = `${keyword}:`;
|
||||
|
||||
const stripComment = (l: string): string | null => {
|
||||
if (!l.startsWith(comment)) return null;
|
||||
return l.substring(comment.length).trimStart();
|
||||
};
|
||||
const isExtra = (l: string): boolean => {
|
||||
const s = stripComment(l);
|
||||
return s !== null && (s.startsWith(extraMarkerUnderscore) || s.startsWith(extraMarkerHyphen));
|
||||
};
|
||||
const isManual = (l: string): boolean => {
|
||||
const s = stripComment(l);
|
||||
return s !== null && s.startsWith(manualMarker);
|
||||
};
|
||||
|
||||
const lines = scriptContent.split("\n");
|
||||
|
||||
let pos = -1;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const l = lines[i];
|
||||
if (l.startsWith(comment) && (l.includes(extraMarker) || l.includes(manualMarker))) {
|
||||
if (isExtra(lines[i]) || isManual(lines[i])) {
|
||||
pos = i;
|
||||
break;
|
||||
}
|
||||
@@ -62,9 +75,11 @@ function extractWorkspaceDepsAnnotation(
|
||||
if (pos === -1) return null;
|
||||
|
||||
const annotationLine = lines[pos];
|
||||
const mode: AnnotationMode = annotationLine.includes(extraMarker) ? "extra" : "manual";
|
||||
const mode: AnnotationMode = isExtra(annotationLine) ? "extra" : "manual";
|
||||
|
||||
const marker = mode === "extra" ? extraMarker : manualMarker;
|
||||
const marker = mode === "extra"
|
||||
? (annotationLine.includes(extraMarkerUnderscore) ? extraMarkerUnderscore : extraMarkerHyphen)
|
||||
: manualMarker;
|
||||
const unparsed = annotationLine.replaceAll(marker, "").replaceAll(comment, "");
|
||||
const external = unparsed
|
||||
.split(",")
|
||||
@@ -185,6 +200,18 @@ def main():
|
||||
expect(r.inline).toEqual("numpy>=1.24.0");
|
||||
});
|
||||
|
||||
test("python: extra-requirements (hyphen) mode", () => {
|
||||
const code = `# extra-requirements: utils
|
||||
#numpy>=1.24.0
|
||||
|
||||
def main():
|
||||
pass`;
|
||||
const r = extractWorkspaceDepsAnnotation(code, "python3")!;
|
||||
expect(r.mode).toEqual("extra");
|
||||
expect(r.external).toEqual(["utils"]);
|
||||
expect(r.inline).toEqual("numpy>=1.24.0");
|
||||
});
|
||||
|
||||
test("python: empty requirements (opt-out)", () => {
|
||||
const code = `# requirements:
|
||||
def main():
|
||||
|
||||
Reference in New Issue
Block a user