This commit is contained in:
Devraj Pal 2026-09-25 11:32:38 +05:30 • committed by GitHub
commit a13e6c4876
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 75 additions and 5 deletions

View file

@ -1716,10 +1716,33 @@ def validate_config_file(config_path: str) -> Path:
# sources, so a large file makes session bring-up slower.
def _split_workspace_spec(spec: str) -> tuple[str, str | None]:
"""Split ``PATH[:DEST]`` into ``(path, dest)``, ``dest`` is ``None`` if absent.
A Windows drive letter's colon (``C:\\path\\file``) is the only colon in a
bare path. ``rpartition`` still finds it, leaving a single-letter ``raw``
with the rest of the path in what would be ``dest``. That collides with a
real single-letter source that declares an absolute ``/workspace/...``
destination (``a:/workspace/input.txt``), so the drive-letter case is only
taken when ``dest`` is not one of those: the explicit form keeps its
existing meaning either way.
"""
raw, sep, dest = spec.rpartition(":")
if not sep or not dest.strip():
return spec, None
if (
re.fullmatch(r"[A-Za-z]", raw)
and dest[:1] in ("\\", "/")
and not dest.startswith("/workspace/")
):
return spec, None
return raw, dest
def _workspace_file_dest(spec: str, source: Path) -> str:
"""Return the workspace-relative destination declared by ``spec``."""
_, sep, dest = spec.rpartition(":")
candidate = dest.strip() if sep and dest.strip() else source.name
_, dest = _split_workspace_spec(spec)
candidate = dest.strip() if dest and dest.strip() else source.name
if candidate.startswith("/") or Path(candidate).is_absolute():
if not candidate.startswith("/workspace/"):
raise ValueError(
@ -1749,8 +1772,7 @@ def resolve_workspace_files(specs: list[str] | None) -> list[dict[str, str]]:
resolved: list[dict[str, str]] = []
seen: dict[str, str] = {}
for spec in specs or []:
raw, sep, dest = spec.rpartition(":")
source_text = raw if sep and dest.strip() else spec
source_text, _ = _split_workspace_spec(spec)
source = Path(source_text.strip()).expanduser()
if not source.is_file():
raise ValueError(f"'{source}' is not an existing file")

View file

@ -2,12 +2,17 @@
from __future__ import annotations
import sys
from typing import TYPE_CHECKING
import pytest
from strix.core.inputs import build_root_task
from strix.interface.utils import read_workspace_files, resolve_workspace_files
from strix.interface.utils import (
_split_workspace_spec,
read_workspace_files,
resolve_workspace_files,
)
if TYPE_CHECKING:
@ -40,6 +45,49 @@ def test_a_declared_destination_is_taken_relative_to_the_workspace(
assert resolved[0]["workspace_path"] == "/workspace/specs/openapi.yaml"
@pytest.mark.parametrize(
("spec", "expected"),
[
(r"C:\temp\wordlist.txt", (r"C:\temp\wordlist.txt", None)),
("C:/temp/wordlist.txt", ("C:/temp/wordlist.txt", None)),
(r"C:\temp\wordlist.txt:dest/file", (r"C:\temp\wordlist.txt", "dest/file")),
],
)
def test_a_windows_drive_letter_is_not_mistaken_for_a_dest_separator(
spec: str, expected: tuple[str, str | None]
) -> None:
assert _split_workspace_spec(spec) == expected
def test_a_single_letter_source_with_a_workspace_destination_is_not_a_drive_letter() -> None:
assert _split_workspace_spec("a:/workspace/input.txt") == ("a", "/workspace/input.txt")
def test_a_single_letter_file_can_declare_an_absolute_workspace_destination(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
(tmp_path / "a").write_text("x", encoding="utf-8")
monkeypatch.chdir(tmp_path)
# A relative, single-letter spec: "a:/workspace/input.txt" is exactly the
# shape rpartition(":") could confuse for a Windows drive letter.
resolved = resolve_workspace_files(["a:/workspace/input.txt"])
assert resolved[0]["workspace_path"] == "/workspace/input.txt"
@pytest.mark.skipif(sys.platform != "win32", reason="tmp_path is only drive-letter-shaped here")
def test_a_bare_windows_path_resolves_end_to_end(tmp_path: Path) -> None:
source = tmp_path / "wordlist.txt"
source.write_text("admin\n", encoding="utf-8")
resolved = resolve_workspace_files([str(source)])
assert resolved == [
{"source_path": str(source.resolve()), "workspace_path": "/workspace/wordlist.txt"}
]
def test_a_missing_file_is_rejected(tmp_path: Path) -> None:
with pytest.raises(ValueError, match="not an existing file"):
resolve_workspace_files([str(tmp_path / "nope.txt")])