Skip to content

cli/command/formatter: assorted fixes and cleanups#6875

Open
thaJeztah wants to merge 4 commits intodocker:masterfrom
thaJeztah:optimize_formatter
Open

cli/command/formatter: assorted fixes and cleanups#6875
thaJeztah wants to merge 4 commits intodocker:masterfrom
thaJeztah:optimize_formatter

Conversation

@thaJeztah
Copy link
Copy Markdown
Member

cli/command/formatter: optimize ContainerContext.Names

Optimize formatting of container name(s);

  • Inline StripNamePrefix in the loop, so that we don't have to
    construct a new slice with names (in most cases only to pick
    the first one).
  • Don't use strings.Split, as it allocates a new slice and we only
    used it to check if the container-name was a legacy-link (contained
    slashes).
  • Use a string-builder to concatenate names when not truncating instead
    of using an intermediate slice (and strings.Join).

cli/command/formatter: Context.postFormat: remove redundant buffer

A tabwriter is backed by a buffer already, because it needs to re-flow columns
based on content written to it. This buffer was added in moby@ea61dac9e6 as
part of a new feature to allow for custom delimiters; neither the patch, nor
code-review on the PR mention the extra buffer, so it likely was just overlooked.

This patch;

  • removes the redundant buffer
  • adds an early return for cases where no tabwriter is used.

cli/command/formatter: add Format.templateString, remove Context.preFormat

The Context.preFormat method normalizes the Format as given by the user,
and handles (e.g.) stripping the "table" prefix and replacing the "json"
format for the actual format ({{json .}}).

The method used a finalFormat field on the Context as intermediate,
and was required to be called before executing the format.

This patch adds a Format.templateString() method that returns the
parsed format instead of storing it on the Context. It is currently
not exported, but something we could consider in future.

cli/command/formatter: NewStats: update GoDoc and add TODO

Update the GoDoc to better align with the actual implementation. The
"idOrName" is used for fuzzy-matching the container, which can result
in multiple stats for the same container:

docker ps --format 'table {{.ID}}\t{{.Names}}'
CONTAINER ID   NAMES
b49e6c21d12e   quizzical_maxwell

docker stats --no-stream quizzical_maxwell b49e6c21d12e b49e6
CONTAINER ID   NAME                CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O        PIDS
b49e6c21d12e   quizzical_maxwell   0.10%     140.8MiB / 7.653GiB   1.80%     3.11MB / 13.4kB   115MB / 1.12MB   28
b49e6c21d12e   quizzical_maxwell   0.10%     140.8MiB / 7.653GiB   1.80%     3.11MB / 13.4kB   115MB / 1.12MB   28
b49e6c21d12e   quizzical_maxwell   0.10%     140.8MiB / 7.653GiB   1.80%     3.11MB / 13.4kB   115MB / 1.12MB   28

We should resolve the canonical ID once, then use that as reference
to prevent duplicates. Various parts in the code compare Container
against "ID" only (not considering "name" or "ID-prefix").

- Human readable description for the release notes

- A picture of a cute animal (not mandatory but encouraged)

Optimize formatting of container name(s);

- Inline `StripNamePrefix` in the loop, so that we don't have to
  construct a new slice with names (in most cases only to pick
  the first one).
- Don't use `strings.Split`, as it allocates a new slice and we only
  used it to check if the container-name was a legacy-link (contained
  slashes).
- Use a string-builder to concatenate names when not truncating instead
  of using an intermediate slice (and `strings.Join`).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
A tabwriter is backed by a buffer already, because it needs to re-flow columns
based on content written to it. This buffer was added in [moby@ea61dac9e6] as
part of a new feature to allow for custom delimiters; neither the patch, nor
code-review on the PR mention the extra buffer, so it likely was just overlooked.

This patch;

- removes the redundant buffer
- adds an early return for cases where no tabwriter is used.

[moby@ea61dac9e6]: moby/moby@ea61dac

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
…ormat

The `Context.preFormat` method normalizes the Format as given by the user,
and handles (e.g.) stripping the "table" prefix and replacing the "json"
format for the actual format (`{{json .}}`).

The method used a `finalFormat` field on the Context as intermediate,
and was required to be called before executing the format.

This patch adds a `Format.templateString()` method that returns the
parsed format instead of storing it on the Context. It is currently
not exported, but something we could consider in future.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Update the GoDoc to better align with the actual implementation. The
"idOrName" is used for fuzzy-matching the container, which can result
in multiple stats for the same container:

    docker ps --format 'table {{.ID}}\t{{.Names}}'
    CONTAINER ID   NAMES
    b49e6c21d12e   quizzical_maxwell

    docker stats --no-stream quizzical_maxwell b49e6c21d12e b49e6
    CONTAINER ID   NAME                CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O        PIDS
    b49e6c21d12e   quizzical_maxwell   0.10%     140.8MiB / 7.653GiB   1.80%     3.11MB / 13.4kB   115MB / 1.12MB   28
    b49e6c21d12e   quizzical_maxwell   0.10%     140.8MiB / 7.653GiB   1.80%     3.11MB / 13.4kB   115MB / 1.12MB   28
    b49e6c21d12e   quizzical_maxwell   0.10%     140.8MiB / 7.653GiB   1.80%     3.11MB / 13.4kB   115MB / 1.12MB   28

We should resolve the canonical ID once, then use that as reference
to prevent duplicates. Various  parts in the code compare Container
against "ID" only (not considering "name" or "ID-prefix").

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
@thaJeztah thaJeztah added this to the 29.3.1 milestone Mar 20, 2026
@thaJeztah thaJeztah added status/2-code-review kind/refactor PR's that refactor, or clean-up code labels Mar 20, 2026
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.08108% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
cli/command/formatter/container.go 72.72% 2 Missing and 1 partial ⚠️
cli/command/container/formatter_stats.go 0.00% 2 Missing ⚠️
cli/command/formatter/formatter.go 91.66% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors and optimizes parts of the CLI formatter layer, focusing on reducing allocations in container name formatting and simplifying template preprocessing / output buffering behavior.

Changes:

  • Adds Format.templateString() to normalize --format inputs (table/json prefixes, whitespace trimming, escape handling) and removes Context.preFormat() / finalFormat.
  • Simplifies Context.postFormat() by removing an extra intermediate buffer and adding an early return when not using the tabwriter.
  • Optimizes ContainerContext.Names() to avoid intermediate slices/splits and reduce allocations; updates NewStats doc/comment to better reflect fuzzy matching behavior.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
cli/command/formatter/formatter.go Introduces Format.templateString(), removes Context.preFormat(), simplifies post-formatting output flow.
cli/command/formatter/formatter_test.go Expands Format tests to cover templateString() normalization and edge cases.
cli/command/formatter/disk_usage.go Removes preFormat() calls to align with the new templateString()-based parsing.
cli/command/formatter/container.go Optimizes container name formatting logic and clarifies truncation behavior in docs.
cli/command/container/formatter_stats.go Updates NewStats signature/docs and adds a FIXME about fuzzy matching duplicates.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +51 to +53
case TableFormatKey:
// "--format table"
return TableFormatKey
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

Format.templateString() special-cases "table" and returns the literal string "table" for templating. That means if a Context ever reaches parseFormat() with Format == TableFormatKey, the rendered output will include the text "table" (instead of an empty template, which was the previous behavior after stripping the prefix). Consider removing this special-case and letting the existing CutPrefix logic handle "table" (producing an empty template), or explicitly returning "" for the TableFormatKey case; update the unit test accordingly.

Suggested change
case TableFormatKey:
// "--format table"
return TableFormatKey

Copilot uses AI. Check for mistakes.
Comment on lines +149 to +165
if c.trunc {
// When printing truncated, we only print a single name.
//
// Pick the first name that's not a legacy link (does not have
// slashes inside the name itself (e.g., "/other-container/link")).
// Normally this would be the first name found.
if strings.IndexByte(name, '/') == -1 {
return name
}
continue
}
if i > 0 {
b.WriteByte(',')
}
b.WriteString(name)
}
return strings.Join(names, ",")
return b.String()
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

ContainerContext.Names() now returns an empty string when c.trunc is true and all entries in c.c.Names contain an internal slash (legacy-link names). Previously, this case would fall back to returning all names joined by commas. To avoid a potential blank NAME/NAMES column, consider adding a fallback (e.g., return the first trimmed name, or join all trimmed names) when no non-legacy name is found.

Copilot uses AI. Check for mistakes.
func NewStats(idOrName string) *Stats {
// FIXME(thaJeztah): "idOrName" is used for fuzzy-matching the container, which can result in multiple stats for the same container.
// We should resolve the canonical ID once, then use that as reference
// to prevent duplicates. Various parts in the code compare Container
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

Minor typo/spacing in the new FIXME comment: "Various parts" has a double space. Consider changing to "Various parts" while touching this comment.

Suggested change
// to prevent duplicates. Various parts in the code compare Container
// to prevent duplicates. Various parts in the code compare Container

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/refactor PR's that refactor, or clean-up code status/2-code-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants