Skip to content

Commit cccccd3

Browse files
author
Tim Huff
committed
respondign to PR feedback
1 parent 3e6fb5c commit cccccd3

2 files changed

Lines changed: 33 additions & 8 deletions

File tree

src/groundlight/cli.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import json
22
import logging
33
import sys
4-
from datetime import datetime
4+
from datetime import date, datetime
5+
from decimal import Decimal
56
from enum import Enum
67
from functools import wraps
78
from importlib.metadata import version as importlib_version
89
from typing import Any, Union
10+
from uuid import UUID
911

1012
import typer
1113
from groundlight_openapi_client.model_utils import OpenApiModel
@@ -15,7 +17,7 @@
1517
from groundlight import ExperimentalApi, Groundlight
1618
from groundlight.client import ApiTokenError
1719

18-
logger = logging.getLogger("groundlight.sdk")
20+
logger = logging.getLogger(__name__)
1921

2022
cli_app = typer.Typer(
2123
context_settings={"help_option_names": ["-h", "--help"], "max_content_width": 800},
@@ -40,7 +42,6 @@ def _main(
4042
context_settings={"help_option_names": ["-h", "--help"], "max_content_width": 800},
4143
)
4244
cli_app.add_typer(experimental_app, name="exp", rich_help_panel="Subcommands")
43-
cli_app.add_typer(experimental_app, name="experimental", hidden=True)
4445

4546

4647
def is_cli_supported_type(annotation):
@@ -67,10 +68,20 @@ def is_cli_representable(annotation) -> bool:
6768

6869

6970
def _json_default(obj: Any) -> Any:
70-
"""Fallback serializer for json.dumps — handles datetime values."""
71-
if isinstance(obj, datetime):
71+
"""Fallback serializer for json.dumps for types the stdlib encoder doesn't handle.
72+
73+
Covers common types that appear in OpenAPI client to_dict() output. Unknown types
74+
fall back to str() rather than raising, so CLI output is always usable.
75+
"""
76+
if isinstance(obj, (datetime, date)):
7277
return obj.isoformat()
73-
raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable")
78+
if isinstance(obj, Decimal):
79+
return float(obj)
80+
if isinstance(obj, UUID):
81+
return str(obj)
82+
if isinstance(obj, Enum):
83+
return obj.value
84+
return str(obj)
7485

7586

7687
def _format_result(result: Any) -> str:
@@ -178,6 +189,7 @@ def wrapper(*args, **kwargs):
178189
"ML Pipelines & Priming",
179190
"Notes",
180191
"Utilities",
192+
"Other",
181193
]
182194

183195
# Maps method names to their rich_help_panel group label for the CLI help output.
@@ -256,14 +268,14 @@ def groundlight():
256268
for name, method in sorted(vars(Groundlight).items(), key=_cli_sort_key):
257269
if callable(method) and not name.startswith("_") and name not in _CLI_EXCLUDED_METHODS:
258270
cli_func = class_func_to_cli(method)
259-
cli_app.command(rich_help_panel=_COMMAND_GROUPS.get(name))(cli_func)
271+
cli_app.command(rich_help_panel=_COMMAND_GROUPS.get(name, "Other"))(cli_func)
260272

261273
for name, method in sorted(vars(ExperimentalApi).items(), key=_cli_sort_key):
262274
if not callable(method) or name.startswith("_") or name in stable_names or name in _CLI_EXCLUDED_METHODS:
263275
continue
264276
try:
265277
cli_func = class_func_to_cli(method, is_experimental=True)
266-
experimental_app.command(rich_help_panel=_COMMAND_GROUPS.get(name))(cli_func)
278+
experimental_app.command(rich_help_panel=_COMMAND_GROUPS.get(name, "Other"))(cli_func)
267279
except Exception as e: # pylint: disable=broad-except
268280
logger.debug("Skipping experimental CLI command '%s': %s", name, e)
269281

test/unit/test_cli.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,19 @@ def test_help():
103103
assert completed_process.returncode == 0
104104

105105

106+
def test_version():
107+
for flag in ("--version", "-v"):
108+
completed_process = subprocess.run(
109+
["groundlight", flag],
110+
stdout=subprocess.PIPE,
111+
stderr=subprocess.PIPE,
112+
text=True,
113+
check=False,
114+
)
115+
assert completed_process.returncode == 0
116+
assert re.match(r"\d+\.\d+\.\d+", completed_process.stdout.strip())
117+
118+
106119
def test_experimental_subcommand():
107120
# Both 'experimental' and 'exp' should resolve to the same subcommand group
108121
for alias in ("experimental", "exp"):

0 commit comments

Comments
 (0)