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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ It keeps a small owned command surface for diagnostics and guidance, and
delegates everything else to the native `aim` executable already available in
the user's environment.

## Installation

```bash
# Using uv (recommended)
uv add aimx

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For a CLI tool like aimx, uv tool install is generally the recommended way to install it for global use, rather than uv add which is intended for project dependencies. Consider updating the recommendation to reflect this best practice.

Suggested change
uv add aimx
uv tool install aimx


# Or using pip
pip install aimx
```

## What aimx owns

- `aimx`
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "aimx"
version = "0.1.0"
version = "0.2.0"
description = "A safe CLI-first companion for native Aim"
readme = "README.md"
requires-python = ">=3.10,<3.13"
Expand Down
2 changes: 1 addition & 1 deletion src/aimx/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__all__ = ["__version__"]

__version__ = "0.1.0"
__version__ = "0.2.0"
17 changes: 13 additions & 4 deletions src/aimx/commands/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
from pathlib import Path
from typing import Any

from aim import Repo
from aim.sdk.types import QueryReportMode


SUPPORTED_TARGETS = {"metrics", "images"}


Expand All @@ -35,6 +31,18 @@ class QueryCommandResult:
error_message: str | None = None


def load_aim_query_support():

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Adding a return type hint to load_aim_query_support would improve code clarity and maintainability, consistent with the rest of the codebase which uses type hints for function signatures.

Suggested change
def load_aim_query_support():
def load_aim_query_support() -> tuple[Any, Any]:

try:
from aim import Repo
from aim.sdk.types import QueryReportMode
except ModuleNotFoundError as error:
raise RuntimeError(
"`aimx query` requires the Python `aim` package in the current environment."
) from error

return Repo, QueryReportMode


def normalize_repo_path(path: Path) -> Path:
if not path.exists():
raise ValueError(f"Repository path does not exist: {path}")
Expand Down Expand Up @@ -108,6 +116,7 @@ def run_query_command(args: list[str]) -> QueryCommandResult:


def collect_query_rows(invocation: QueryInvocation, repo_path: Path) -> list[dict[str, Any]]:
Repo, QueryReportMode = load_aim_query_support()
repo = Repo(str(repo_path))

if invocation.target == "metrics":
Expand Down
42 changes: 42 additions & 0 deletions tests/integration/test_missing_python_aim_package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from __future__ import annotations

import importlib
import sys


def _reload_main_without_python_aim(monkeypatch):
monkeypatch.setitem(sys.modules, "aim", None)
monkeypatch.delitem(sys.modules, "aim.sdk", raising=False)
monkeypatch.delitem(sys.modules, "aim.sdk.types", raising=False)
sys.modules.pop("aimx.__main__", None)
sys.modules.pop("aimx.cli", None)
sys.modules.pop("aimx.commands.query", None)
return importlib.import_module("aimx.__main__").main


def test_owned_commands_still_work_when_python_aim_package_is_missing(
capsys, monkeypatch
) -> None:
monkeypatch.setenv("PATH", "")

main = _reload_main_without_python_aim(monkeypatch)
exit_code = main(["version"])

captured = capsys.readouterr()
assert exit_code == 0
assert "aimx 0.1.0" in captured.out

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The version string in this assertion is outdated. Since the project version was bumped to 0.2.0 in this pull request (see pyproject.toml and src/aimx/__init__.py), this test will fail unless updated to match the new version.

Suggested change
assert "aimx 0.1.0" in captured.out
assert "aimx 0.2.0" in captured.out

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Assert current package version in integration test

This test hard-codes "aimx 0.1.0" even though the same commit bumps the project version to 0.2.0 (pyproject.toml and aimx.__version__), so the test will fail on every run immediately after this change. The check should derive the expected version dynamically (or match 0.2.0) so CI verifies the missing-aim behavior instead of failing due to stale version text.

Useful? React with 👍 / 👎.



def test_query_reports_actionable_error_when_python_aim_package_is_missing(
capsys, monkeypatch, tmp_path
) -> None:
monkeypatch.setenv("PATH", "")

main = _reload_main_without_python_aim(monkeypatch)
exit_code = main(
["query", "metrics", "metric.name == 'loss'", "--repo", str(tmp_path)]
)

captured = capsys.readouterr()
assert exit_code == 2
assert "requires the Python `aim` package" in captured.err
Loading