Skip to content
Open
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
f5d863b
Improving the list outputs and adding new formats
znegrin Jul 30, 2025
78af737
Merge develop into feat/improved-cli-tables
znegrin Jul 30, 2025
4ecfdfc
Fix type annotation for overflow parameter in table_utils
znegrin Jul 30, 2025
6403d92
Update service connector table to bring back previous data
znegrin Aug 1, 2025
4514cfc
Merge branch 'develop' into feat/improved-cli-tables
znegrin Aug 1, 2025
8f361b6
Fix docstring issues in CLI table functions
znegrin Aug 1, 2025
cd2b987
Fix remaining docstring issues in CLI functions
znegrin Aug 1, 2025
6604636
Fix missing parameter and return documentation in CLI docstrings
znegrin Aug 1, 2025
cea4272
Fix mypy type annotation issues across all CLI files
znegrin Aug 1, 2025
ebebd80
Apply code formatting to CLI files
znegrin Aug 1, 2025
038940d
Merge branch 'develop' into feat/improved-cli-tables
bcdurak Aug 4, 2025
7bed4be
fix one test
bcdurak Aug 4, 2025
bf13b44
removed unused import
bcdurak Aug 6, 2025
fbeef0c
new module for the CLI
bcdurak Aug 6, 2025
15d2247
merged develop
bcdurak Aug 6, 2025
783304d
formatting
bcdurak Aug 6, 2025
a0b63dd
Merge branch 'develop' into feat/improved-cli-tables
bcdurak Aug 6, 2025
a36632a
new changes
bcdurak Aug 7, 2025
ff9acec
removing warning suppresion
bcdurak Aug 7, 2025
6fb4e3c
Merge branch 'develop' into feat/improved-cli-tables
bcdurak Aug 7, 2025
79e75a7
checkpoint
bcdurak Aug 7, 2025
4eea33f
some other fixes
bcdurak Aug 7, 2025
8fa6b5a
merged
bcdurak Aug 7, 2025
c383943
docstring changes
bcdurak Aug 27, 2025
783f8eb
merged develop, resolved conflicts
bcdurak Aug 29, 2025
81045c5
removed unused stuff
bcdurak Aug 29, 2025
277e774
new formatting
bcdurak Sep 1, 2025
932d61d
docstrings and linting
bcdurak Sep 1, 2025
6ef6bc7
Merge branch 'develop' into feat/improved-cli-tables
bcdurak Sep 1, 2025
39a7c43
formatting linting
bcdurak Sep 1, 2025
06e2ad5
formatting
bcdurak Sep 1, 2025
494df98
add id to pipeline runs
bcdurak Sep 1, 2025
ffe3bf1
some more formatting
bcdurak Sep 1, 2025
1ea0f32
removed unneccessary tests
bcdurak Sep 1, 2025
0341fd5
Merge branch 'develop' into feat/improved-cli-tables
bcdurak Sep 11, 2025
44a7b1c
fixed merge conflicts
bcdurak Sep 17, 2025
99399c0
some checkpoint
bcdurak Sep 17, 2025
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
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[tool.poetry]
name = "zenml"
version = "0.84.3"
packages = [{ include = "zenml", from = "src" }]
packages = [{ include = "zenml", from = "src" }, { include = "zenml_cli", from = "src" }]
description = "ZenML: Write production-ready ML code."
authors = ["ZenML GmbH <[email protected]>"]
readme = "README.md"
Expand Down Expand Up @@ -38,7 +38,7 @@ exclude = [
include = ["src/zenml", "*.txt", "*.sh", "*.md"]

[tool.poetry.scripts]
zenml = "zenml.cli.cli:cli"
zenml = "zenml_cli:cli"

[tool.poetry.dependencies]
alembic = { version = ">=1.8.1,<=1.15.2" }
Expand Down
117 changes: 77 additions & 40 deletions src/zenml/cli/artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,19 @@

from zenml.cli import utils as cli_utils
from zenml.cli.cli import TagGroup, cli
from zenml.cli.utils import (
enhanced_list_options,
)
from zenml.client import Client
from zenml.console import console
from zenml.enums import CliCategories
from zenml.logger import get_logger
from zenml.models import ArtifactFilter, ArtifactVersionFilter
from zenml.models.v2.core.artifact import ArtifactResponse
from zenml.models.v2.core.artifact_version import ArtifactVersionResponse
from zenml.models import (
ArtifactFilter,
ArtifactResponse,
ArtifactVersionFilter,
ArtifactVersionResponse,
)
from zenml.utils.pagination_utils import depaginate

logger = get_logger(__name__)
Expand All @@ -35,25 +42,55 @@ def artifact() -> None:
"""Commands for interacting with artifacts."""


@cli_utils.list_options(ArtifactFilter)
@enhanced_list_options(
Copy link
Contributor

Choose a reason for hiding this comment

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

All comments on this apply to all CLI commands that are modified as part of this PR.

ArtifactFilter,
default_columns=["id", "name", "latest_version_name", "user", "created"],
)
@artifact.command("list", help="List all artifacts.")
def list_artifacts(**kwargs: Any) -> None:
"""List all artifacts.

Args:
**kwargs: Keyword arguments to filter artifacts by.
"""
artifacts = Client().list_artifacts(**kwargs)
# Extract table options from kwargs
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh really

table_kwargs = cli_utils.extract_table_options(kwargs)

with console.status("Listing artifacts..."):
artifacts = Client().list_artifacts(**kwargs)

if not artifacts:
cli_utils.declare("No artifacts found.")
Copy link
Contributor

Choose a reason for hiding this comment

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

When there are no entities found but the requested output format is JSON, I would expect either Null or []/{} written to stdout. WDYT?

return

to_print = []
for artifact in artifacts:
to_print.append(_artifact_to_print(artifact))
def enrichment_func(
item: ArtifactResponse, result: Dict[str, Any]
) -> Dict[str, Any]:
"""Enrich the artifact data with the latest version name and id.

Args:
item: The artifact response.
result: The result dictionary.

Returns:
The enriched result dictionary.
"""
result.update(
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like this function should either

  • modify the result dict in place like you do here
  • return a modified version
    but not both

{
"latest_version_name": item.latest_version_name,
Copy link
Contributor

Choose a reason for hiding this comment

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

Also, what is the purpose of this enrichment function? It doesn't respect the columns arg that the user requested, so I assume that is handled later? If that is correct, how are users supposed to know that latest_version_id is even a possibility of a column that they can pass?

"latest_version_id": item.latest_version_id,
}
)
return result

cli_utils.print_table(to_print)
artifact_data = cli_utils.prepare_data_from_responses(
artifacts.items, enrichment_func=enrichment_func
)

# Handle table output with enhanced system and pagination
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you please remove all those garbage AI comments from this PR. I haven't found a single useful one yet.

cli_utils.handle_table_output(
artifact_data, page=artifacts, **table_kwargs
)


@artifact.command("update", help="Update an artifact.")
Expand Down Expand Up @@ -115,25 +152,50 @@ def version() -> None:
"""Commands for interacting with artifact versions."""


@cli_utils.list_options(ArtifactVersionFilter)
@enhanced_list_options(
ArtifactVersionFilter,
default_columns=["id", "name", "version", "type", "user", "created"],
)
@version.command("list", help="List all artifact versions.")
def list_artifact_versions(**kwargs: Any) -> None:
"""List all artifact versions.

Args:
**kwargs: Keyword arguments to filter artifact versions by.
"""
artifact_versions = Client().list_artifact_versions(**kwargs)
# Extract table options from kwargs
table_kwargs = cli_utils.extract_table_options(kwargs)

with console.status("Listing artifact versions..."):
artifact_versions = Client().list_artifact_versions(**kwargs)

if not artifact_versions:
cli_utils.declare("No artifact versions found.")
return

to_print = []
for artifact_version in artifact_versions:
to_print.append(_artifact_version_to_print(artifact_version))
def enrichment_func(
item: ArtifactVersionResponse, result: Dict[str, Any]
) -> Dict[str, Any]:
"""Enrich the artifact version data with the artifact name.

Args:
item: The artifact version response.
result: The result dictionary.

Returns:
The enriched result dictionary.
"""
result.update({"name": item.artifact.name})
return result

artifact_version_data = cli_utils.prepare_data_from_responses(
artifact_versions.items, enrichment_func=enrichment_func
)

cli_utils.print_table(to_print)
# Handle table output with enhanced system and pagination
cli_utils.handle_table_output(
artifact_version_data, page=artifact_versions, **table_kwargs
)


@version.command("update", help="Update an artifact version.")
Expand Down Expand Up @@ -294,28 +356,3 @@ def prune_artifacts(
f"Failed to delete artifact version {unused_artifact_version.id}: {str(e)}"
)
cli_utils.declare("All unused artifacts and artifact versions deleted.")


def _artifact_version_to_print(
artifact_version: ArtifactVersionResponse,
) -> Dict[str, Any]:
return {
"id": artifact_version.id,
"name": artifact_version.artifact.name,
"version": artifact_version.version,
"uri": artifact_version.uri,
"type": artifact_version.type,
"materializer": artifact_version.materializer,
"data_type": artifact_version.data_type,
"tags": [t.name for t in artifact_version.tags],
}


def _artifact_to_print(
artifact_version: ArtifactResponse,
) -> Dict[str, Any]:
return {
"id": artifact_version.id,
"name": artifact_version.name,
"tags": [t.name for t in artifact_version.tags],
}
28 changes: 21 additions & 7 deletions src/zenml/cli/authorized_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@

from zenml.cli import utils as cli_utils
from zenml.cli.cli import TagGroup, cli
from zenml.cli.utils import list_options
from zenml.cli.utils import (
enhanced_list_options,
prepare_data_from_responses,
)
from zenml.client import Client
from zenml.console import console
from zenml.enums import CliCategories
Expand Down Expand Up @@ -56,26 +59,37 @@ def describe_authorized_device(id_or_prefix: str) -> None:
)


@enhanced_list_options(
OAuthDeviceFilter,
default_columns=["status", "ip_address", "hostname", "os", "created"],
)
@authorized_device.command(
"list", help="List all authorized devices for the current user."
)
@list_options(OAuthDeviceFilter)
def list_authorized_devices(**kwargs: Any) -> None:
"""List all authorized devices.

Args:
**kwargs: Keyword arguments to filter authorized devices.
"""
with console.status("Listing authorized devices...\n"):
# Extract table options from kwargs
table_kwargs = cli_utils.extract_table_options(kwargs)

with console.status("Listing authorized devices..."):
devices = Client().list_authorized_devices(**kwargs)

if not devices.items:
cli_utils.declare("No authorized devices found for this filter.")
cli_utils.declare("No authorized devices found.")
return

cli_utils.print_pydantic_models(
devices,
columns=["id", "status", "ip_address", "hostname", "os"],
# Use centralized data preparation
device_data = prepare_data_from_responses(devices.items)

# Handle table output with enhanced system and pagination
cli_utils.handle_table_output(
data=device_data,
page=devices,
**table_kwargs,
)


Expand Down
27 changes: 20 additions & 7 deletions src/zenml/cli/code_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@

from zenml.cli import utils as cli_utils
from zenml.cli.cli import TagGroup, cli
from zenml.cli.utils import list_options
from zenml.cli.utils import (
enhanced_list_options,
prepare_data_from_responses,
)
from zenml.client import Client
from zenml.code_repositories import BaseCodeRepository
from zenml.config.source import Source
Expand Down Expand Up @@ -188,24 +191,34 @@ def describe_code_repository(name_id_or_prefix: str) -> None:
)


@enhanced_list_options(
CodeRepositoryFilter, default_columns=["name", "type", "url", "created"]
)
@code_repository.command("list", help="List all connected code repositories.")
@list_options(CodeRepositoryFilter)
def list_code_repositories(**kwargs: Any) -> None:
"""List all connected code repositories.

Args:
**kwargs: Keyword arguments to filter code repositories.
"""
with console.status("Listing code repositories...\n"):
# Extract table options from kwargs
table_kwargs = cli_utils.extract_table_options(kwargs)

with console.status("Listing code repositories..."):
repos = Client().list_code_repositories(**kwargs)

if not repos.items:
cli_utils.declare("No code repositories found for this filter.")
cli_utils.declare("No code repositories found.")
return

cli_utils.print_pydantic_models(
repos,
exclude_columns=["created", "updated", "user", "project"],
# Use centralized data preparation
repo_data = prepare_data_from_responses(repos.items)

# Handle table output with enhanced system
cli_utils.handle_table_output(
data=repo_data,
page=repos,
**table_kwargs,
)


Expand Down
2 changes: 1 addition & 1 deletion src/zenml/cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied. See the License for the specific language governing
# permissions and limitations under the License.
"""CLI for manipulating ZenML local and global config file."""
"""CLI for ZenML analytics and logging configuration."""

import click

Expand Down
Loading
Loading