Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ name: CI
on:
push:
pull_request:
workflow_dispatch:
schedule:
# Run optional/heavier integration tests weekly and on demand.
- cron: "0 3 * * 1"

jobs:
test:
Expand Down Expand Up @@ -32,7 +36,7 @@ jobs:
run: ruff check .

# Optional but recommended: verifies console scripts + dispatch without launching GUI.
# NOTE: calling plain `cosmo` (no args) attempts to start the GUI by default. [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)
# NOTE: calling plain `cosmo` (no args) attempts to start the GUI by default.
- name: CLI sanity check (no GUI)
run: |
cosmo --version
Expand Down Expand Up @@ -74,4 +78,4 @@ jobs:
- name: Run integration tests
env:
MPLBACKEND: Agg
run: pytest -q -m integration
run: pytest -q -m integration
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ python -m pip install -e . --group dev
> tools explicitly: `pip install -e . pytest ruff` (adding `betterosi mcap` for
> MCAP integration tests).

> Tip 1: The **GUI** is launced with `cosmo` or `cosmo gui`.
> Tip 2: During development with downloaded repro, all `cosmo` commands can be replaced with `python run_cosmo.py ...`, `cosmo.cmd ...` or `cosmo.ps1 ...`. `python run_gui.py` always starts the **GUI**.
> Tip 1: The **GUI** is launched with `cosmo` or `cosmo gui`.
> Tip 2: In headless environments, prefer explicit subcommands such as `cosmo convert ...` rather than plain `cosmo`, which may start the GUI.

Convert using [ORBIT](https://github.com/RI-SE/ORBIT) georef as the primary pixel→ground mapping (**Convert** tab in the **GUI**):

Expand Down Expand Up @@ -81,7 +81,7 @@ Calibration outputs are written to outputs/ as:
- <base_name>_calibration.json
- <base_name>_homography_fit_summary.json
- <base_name>_homography_fit_residuals.png
- <base_name>_overlay_markers_on_image.png (only if --image is provided)
- <base_name>_overlay_markers_on_image.png (only if --image is provided)

Use the calibration file for conversion fallback:

Expand Down
11 changes: 5 additions & 6 deletions docs/getting-started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,16 @@ cosmo
cosmo gui
```

Development shortcut (either of):
Use the installed console script:

```bash
python run_gui.py # Start GUI
python run_cosmo.py gui # Same behaviour as cosmo (when installed)
cosmo.cmd gui # Same behaviour in Windows cmd termial as cosmo (when installed)
cosmo.ps1 gui # Same behaviour in Windows PowerShell termial as cosmo (when installed)
cosmo gui
# or simply:
cosmo
```
---
## CLI
> During development `cosmo ...` can be replaced with `python run_cosmo ...`, `cosmo.exe ...` or `cosmo.ps1 ...` without needing to install cosmo, e.g., `python run_cosmo.py convert ...`. To install for development use `uv sync --group dev` or `pip install -e . --group dev`.
> For development, install the package first (for example `uv sync --group dev` or `pip install -e . --group dev`) and then use the `cosmo ...` console script.
---
### Convert (recommended: ORBIT georef)

Expand Down
4 changes: 2 additions & 2 deletions docs/how-to/calibration.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The calibration JSON is written as:
Other helpful artifacts:
- <base_name>_homography_fit_summary.json
- <base_name>_homography_fit_residuals.png
- <base_name>_overlay_markers_on_image.png (only if --image was supplied)
- <base_name>_overlay_markers_on_image.png (only if --image was supplied)

## Step 3 — Convert using calibration

Expand All @@ -31,4 +31,4 @@ cosmo convert scenario.json \
If results look wrong, check the calibration run’s run_summary.json for:

- RMSE and inlier count
- notes about missing plots/overlays
- notes about missing plots/overlays
2 changes: 1 addition & 1 deletion docs/reference/inputs-openlabel.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Input: OpenLABEL (expected structure)

COSMO reads OpenLABEL JSON and extracts object metadata and per-frame rotated bounding boxes (rbbox).
COSMO reads OpenLABEL JSON and extracts object metadata and per-frame rotated bounding boxes (rbbox).

## Required content
COSMO expects, per frame, objects that contain an `rbbox` with at least five values:
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/outputs-omega-prime.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Outputs: Omega‑Prime CSV + OSI/MCAP

COSMO writes all outputs inside a per-run folder under `runs/` by default.
COSMO writes all outputs inside a per-run folder under `runs/` by default.

---

Expand Down
6 changes: 3 additions & 3 deletions docs/user_guide/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ cosmo convert scenario.json \
--xy-offset 1.2 -0.5 \
-o runs/
```
Available flags: `--swap-xy`, `--flip-x`, `--flip-y`, `--xy-offset DX DY`, `--yaw-offset-deg DEG`.
Available flags: `--swap-xy`, `--flip-x`, `--flip-y`, `--xy-offset DX DY`, `--yaw-offset-deg DEG`.
### Run naming & JSON output
```bash
cosmo convert scenario.json --run-name saro_roundabout -o runs/
Expand Down Expand Up @@ -144,13 +144,13 @@ cosmo calibrate --inputs pixel_pairs.csv visual_markers.csv map.xodr \

##### Useful knobs
- --ransac-thresh-m (default 0.50) — RANSAC distance threshold in meters --fps,
- --image-width, --image-height
- --image-width, --image-height

##### Calibration outputs
Calibration writes stem-based files under outputs/:

- <base_name>_calibration.json
- <base_name>_homography_fit_summary.json
- <base_name>_homography_fit_residuals.png
- <base_name>_overlay_markers_on_image.png (only if --image is provided)
- <base_name>_overlay_markers_on_image.png (only if --image is provided)

4 changes: 2 additions & 2 deletions src/cosmo/converters/openlabel_to_omega.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ def _collect_all_dims() -> Dict[str, Tuple[float, float, float]]:
# ----------------------------
# IMPORTANT:
# Use betterosi.Writer as a context manager to ensure MCAP footer/summary are finalized properly. [1](https://deepwiki.com/ika-rwth-aachen/omega-prime/3.1-loading-and-saving-recordings)[4](https://ika-rwth-aachen.github.io/omega-prime/notebooks/tutorial/)
# Also write topic names without leading "/" to match common omega-prime usage. [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py)[2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py)
# Also write topic names without leading "/" to match common omega-prime usage.

def _write_map(writer_mcap):
if odr_path and os.path.isfile(odr_path):
Expand Down Expand Up @@ -752,7 +752,7 @@ def _write_ground_truth(writer_mcap, t_sec: float, total_nanos: int, moving_obje
gt.proj_string = _gt_proj_string
if _gt_country_code:
gt.country_code = _gt_country_code
# Provide log_time explicitly; readers often build indices from it. [4](https://ika-rwth-aachen.github.io/omega-prime/notebooks/tutorial/)[3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py)
# Provide log_time explicitly; readers often build indices from it. [4](https://ika-rwth-aachen.github.io/omega-prime/notebooks/tutorial/)
writer_mcap.add(gt, topic="ground_truth", log_time=total_nanos)

# If MCAP enabled, write with context manager; else only build CSV rows
Expand Down
2 changes: 1 addition & 1 deletion src/cosmo/gui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pathlib import Path
from typing import Optional

# Qt binding selection: PyQt5 → PySide6 → PyQt6 (same as your current GUI). [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_openlabel_to_omega.py)
# Qt binding selection: PyQt5 → PySide6 → PyQt6 (same as your current GUI).
try:
from PyQt5 import QtCore, QtGui, QtWidgets
_QT_API = "PyQt5"
Expand Down
2 changes: 1 addition & 1 deletion src/cosmo/gui/workers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from typing import Callable

# Qt binding selection (same preference order as your current GUI) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_openlabel_to_omega.py)
# Qt binding selection (same preference order as your current GUI)
try:
from PyQt5 import QtCore
_Signal = QtCore.pyqtSignal
Expand Down
4 changes: 2 additions & 2 deletions tests/cli_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ def run_cosmo(args: list[str], *, cwd: Path) -> subprocess.CompletedProcess[str]
env.setdefault("MPLBACKEND", "Agg")

if os.name == "nt":
cmd = [sys.executable, "-m", "cosmo.cli.main", *args] # real entrypoint logic [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)
cmd = [sys.executable, "-m", "cosmo.cli.main", *args] # real entrypoint logic
else:
exe = shutil.which("cosmo")
cmd = [exe, *args] if exe else [sys.executable, "-m", "cosmo.cli.main", *args] # same entrypoint [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)
cmd = [exe, *args] if exe else [sys.executable, "-m", "cosmo.cli.main", *args] # same entrypoint

return subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True, env=env)

Expand Down
6 changes: 3 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ def run_cosmo(args: list[str], *, cwd: Path) -> subprocess.CompletedProcess[str]
Fallback: if the console script isn't on PATH (rare in CI), run via module entrypoint:
python -m cosmo.cli.main <args...>

NOTE: Never call plain `cosmo` with no args in CI, because cosmo.cli.main defaults to GUI. [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)
NOTE: Never call plain `cosmo` with no args in CI, because cosmo.cli.main defaults to GUI.
"""
env = dict(os.environ)
env.setdefault("MPLBACKEND", "Agg") # CI already sets this, but keep it deterministic. [1](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101)
env.setdefault("MPLBACKEND", "Agg") # CI already sets this, but keep it deterministic.

exe = shutil.which("cosmo")
if exe:
cmd = [exe, *args]
else:
# Still the real entrypoint, just executed as a module.
cmd = [os.environ.get("PYTHON", "python"), "-m", "cosmo.cli.main", *args] # dispatch logic in main.py [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)
cmd = [os.environ.get("PYTHON", "python"), "-m", "cosmo.cli.main", *args] # dispatch logic in main.py

return subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True, env=env)

Expand Down
2 changes: 1 addition & 1 deletion tests/test_calibrate_cli_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ def test_calibrate_rejects_mixed_input_styles(tmp_path: Path):
cwd=tmp_path,
)
assert res.returncode != 0
assert "Mixed input styles detected" in (res.stderr + res.stdout) # error text comes from argparse error [2](blob:https://www.microsoft365.com/c655cd7d-9ac4-4928-84e7-b9a8f04405bd)
assert "Mixed input styles detected" in (res.stderr + res.stdout) # error text comes from argparse error
8 changes: 4 additions & 4 deletions tests/test_calibration_compute_whitebox.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import numpy as np
import pytest

from cosmo.calibration.compute import ( # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py)
from cosmo.calibration.compute import ( #
compute_calibration,
write_calibration_outputs,
)
Expand Down Expand Up @@ -75,7 +75,7 @@ def test_write_calibration_outputs_writes_expected_json_schema(tmp_path: Path):
fps=30.0,
image_width=3840,
image_height=2160,
residuals_png_path=str(resid_png), # optional; may become None on plot errors [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py)
residuals_png_path=str(resid_png), # optional; may become None on plot errors
overlay_png_path=None,
image_path=None,
openlabel_path=None,
Expand All @@ -85,7 +85,7 @@ def test_write_calibration_outputs_writes_expected_json_schema(tmp_path: Path):
assert Path(outs.summary_json_path).is_file()

calib = json.loads(Path(outs.calibration_json_path).read_text(encoding="utf-8"))
# Required keys written by write_calibration_outputs [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py)
# Required keys written by write_calibration_outputs
for k in ("fps", "image_width", "image_height", "homography", "intrinsics", "extrinsics", "default_dimensions_m"):
assert k in calib
H = calib["homography"]
Expand All @@ -95,7 +95,7 @@ def test_write_calibration_outputs_writes_expected_json_schema(tmp_path: Path):
for k in ("rmse_m", "inliers_count", "pairs_used", "pixel_points", "world_points_ENU_m", "homography"):
assert k in summary

# Residual plot can be omitted if matplotlib errors; if present, ensure file exists [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py)
# Residual plot can be omitted if matplotlib errors; if present, ensure file exists
if outs.residuals_png_path is not None:
assert Path(outs.residuals_png_path).is_file()

Expand Down
4 changes: 2 additions & 2 deletions tests/test_cli_contracts_not_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def test_calibrate_rejects_mixed_input_styles(tmp_path: Path):
vm.write_text("point_name,E,N\np1,0,0\np2,1,0\np3,0,1\np4,1,1\n", encoding="utf-8")
odr.write_text("<OpenDRIVE></OpenDRIVE>", encoding="utf-8")

# Mix two styles intentionally -> should error (logic is in calibrate.py) [2](blob:https://www.microsoft365.com/c655cd7d-9ac4-4928-84e7-b9a8f04405bd)
# Mix two styles intentionally -> should error (logic is in calibrate.py)
res = run_cosmo(
["calibrate", "--inputs", str(pp), str(vm), str(odr), "--pixel-pairs", str(pp)],
cwd=tmp_path,
Expand All @@ -27,7 +27,7 @@ def test_convert_rejects_positional_and_flag_input(tmp_path: Path):
ol = tmp_path / "openlabel.json"
ol.write_text(json.dumps({"openlabel": {"objects": {}, "frames": {}}}), encoding="utf-8")

# convert.py rejects positional + --input together [1](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101)
# convert.py rejects positional + --input together
res = run_cosmo(["convert", str(ol), "--input", str(ol), "--no-mcap"], cwd=tmp_path)
assert res.returncode != 0
assert "Provide either a positional input OR --input/--openlabel" in (res.stderr + res.stdout)
6 changes: 3 additions & 3 deletions tests/test_cli_smoke_calibrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def test_cli_calibrate_smoke(tmp_path: Path):
Smoke test for the real CLI:
cosmo calibrate --inputs pixel_pairs.csv visual_markers.csv map.xodr --json

Uses --inputs style and --json output exactly as supported by cosmo.cli.calibrate. [3](blob:https://www.microsoft365.com/c655cd7d-9ac4-4928-84e7-b9a8f04405bd)
Uses --inputs style and --json output exactly as supported by cosmo.cli.calibrate.
"""
# Minimal input CSVs
pixel_pairs_csv = tmp_path / "pixel_pairs.csv"
Expand All @@ -39,7 +39,7 @@ def test_cli_calibrate_smoke(tmp_path: Path):
encoding="utf-8",
)

# Dummy OpenDRIVE file (required arg in CLI contract) [3](blob:https://www.microsoft365.com/c655cd7d-9ac4-4928-84e7-b9a8f04405bd)
# Dummy OpenDRIVE file (required arg in CLI contract)
odr_path = tmp_path / "map.xodr"
odr_path.write_text("<OpenDRIVE></OpenDRIVE>", encoding="utf-8")

Expand Down Expand Up @@ -78,7 +78,7 @@ def test_cli_calibrate_smoke(tmp_path: Path):

payload = extract_json_from_stdout(res.stdout)

# CLI prints asdict(result) when --json is provided. [3](blob:https://www.microsoft365.com/c655cd7d-9ac4-4928-84e7-b9a8f04405bd)
# CLI prints asdict(result) when --json is provided.
run_dir = Path(payload["run_dir"])
outputs_dir = Path(payload.get("outputs_dir") or payload["run_dir"])
calib_path = Path(payload["calibration_json_path"])
Expand Down
4 changes: 2 additions & 2 deletions tests/test_cli_smoke_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def test_cli_convert_smoke_csv_only(tmp_path: Path):
Smoke test for the real CLI:
cosmo convert --input openlabel.json --calibration Calibration.json --no-mcap --json

Uses flags from cosmo.cli.convert and disables MCAP for CI stability. [1](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101)
Uses flags from cosmo.cli.convert and disables MCAP for CI stability.
"""
# Minimal OpenLABEL input (one object, one frame, rbbox)
openlabel = {
Expand Down Expand Up @@ -64,7 +64,7 @@ def test_cli_convert_smoke_csv_only(tmp_path: Path):
str(out_dir),
"--run-name",
"smoke_convert",
"--no-mcap", # convert CLI uses BooleanOptionalAction; --no-mcap disables MCAP. [1](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101)
"--no-mcap", # convert CLI uses BooleanOptionalAction; --no-mcap disables MCAP.
"--json",
],
cwd=tmp_path,
Expand Down
8 changes: 4 additions & 4 deletions tests/test_console_scripts_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,19 @@ def test_console_scripts_exist_and_help_works():
assert cosmo_calibrate, "Expected 'cosmo-calibrate' on PATH or in venv bin dir after install."
assert cosmo_gui, "Expected 'cosmo-gui' on PATH or in venv bin dir after install."

# Always safe: module entrypoint tests real main.py behavior. [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)
# Always safe: module entrypoint tests real main.py behavior.
res = _run([sys.executable, "-m", "cosmo.cli.main", "--version"])
assert res.returncode == 0, f"cosmo --version failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}"
assert res.stdout.strip(), "cosmo --version produced no output."

# Dispatch help via main.py (convert/calibrate are subcommands). [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)
# Dispatch help via main.py (convert/calibrate are subcommands).
res = _run([sys.executable, "-m", "cosmo.cli.main", "convert", "--help"])
assert res.returncode == 0, f"cosmo convert --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}"

res = _run([sys.executable, "-m", "cosmo.cli.main", "calibrate", "--help"])
assert res.returncode == 0, f"cosmo calibrate --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}"

# Direct entrypoints: run via module on Windows to avoid blocked .EXE shims. [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)[2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)
# Direct entrypoints: run via module on Windows to avoid blocked .EXE shims.
if os.name == "nt":
res = _run([sys.executable, "-m", "cosmo.cli.convert", "--help"])
assert res.returncode == 0, f"python -m cosmo.cli.convert --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}"
Expand All @@ -76,4 +76,4 @@ def test_console_scripts_exist_and_help_works():
res = _run([cosmo_calibrate, "--help"])
assert res.returncode == 0, f"cosmo-calibrate --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}"

# Do NOT execute cosmo-gui in tests: it launches GUI immediately and can fail headless. [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)[3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)
# Do NOT execute cosmo-gui in tests: it launches GUI immediately and can fail headless.
2 changes: 1 addition & 1 deletion tests/test_convert_alignment_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ def test_convert_accepts_alignment_flags(tmp_path: Path):
"--no-mcap", "--swap-xy", "--flip-x", "--flip-y", "--xy-offset", "1.0", "-2.0", "--yaw-offset-deg", "90"],
cwd=tmp_path,
)
assert res.returncode == 0 # should be accepted and run successfully [3](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101)
assert res.returncode == 0 # should be accepted and run successfully
2 changes: 1 addition & 1 deletion tests/test_convert_app_e2e_not_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import json
from pathlib import Path

from cosmo.app.convert_app import ( # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py)
from cosmo.app.convert_app import ( #
ConvertConfig,
run_convert,
)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_convert_cli_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ def test_convert_rejects_positional_and_flag_input(tmp_path: Path):
cwd=tmp_path,
)
assert res.returncode != 0
assert "Provide either a positional input OR --input/--openlabel" in (res.stderr + res.stdout) # emitted by ap.error [3](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101)
assert "Provide either a positional input OR --input/--openlabel" in (res.stderr + res.stdout) # emitted by ap.error
Loading