Remove unused parser resource plumbing
This commit is contained in:
@@ -76,6 +76,8 @@ rendering. A session owns the mutable backend tree for one invocation; parsing
|
|||||||
and presentation reuse that session. The adapter returns raw values and neutral
|
and presentation reuse that session. The adapter returns raw values and neutral
|
||||||
syntax failures to core. It does not own the registry, typed input preparation,
|
syntax failures to core. It does not own the registry, typed input preparation,
|
||||||
integration declarations, provider acquisition, or native report rendering.
|
integration declarations, provider acquisition, or native report rendering.
|
||||||
|
Parser options carry flag-alias selection; invocation context and streams stay
|
||||||
|
with core, while backend output destinations discard and help uses a local buffer.
|
||||||
Cobra and pflag imports remain inside this package.
|
Cobra and pflag imports remain inside this package.
|
||||||
|
|
||||||
## Optional modules
|
## Optional modules
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Consolidated audit catalog
|
# Consolidated audit catalog
|
||||||
|
|
||||||
Status: **14 distinct open findings: 14 P3**, consolidated from 30
|
Status: **13 distinct open findings: 13 P3**, consolidated from 30
|
||||||
numbered entries in seven audit documents. Reviewed audit state:
|
numbered entries in seven audit documents. Reviewed audit state:
|
||||||
`db732e3a1de55c844f77870e1c6db683c2685d5d`.
|
`db732e3a1de55c844f77870e1c6db683c2685d5d`.
|
||||||
|
|
||||||
@@ -257,6 +257,10 @@ consumer compatibility or migration work is needed.
|
|||||||
|
|
||||||
## F12 — P3: Parser sessions carry unused execution resources
|
## F12 — P3: Parser sessions carry unused execution resources
|
||||||
|
|
||||||
|
**Complete.** Unused execution and parser resource plumbing was removed; the
|
||||||
|
backend retains discard destinations and buffered help. Focused routing, help,
|
||||||
|
context-forwarding and session-isolation tests passed.
|
||||||
|
|
||||||
Core and the Cobra adapter duplicate stream normalization and retain context and
|
Core and the Cobra adapter duplicate stream normalization and retain context and
|
||||||
streams through parser options and backend nodes, although the adapter parses
|
streams through parser options and backend nodes, although the adapter parses
|
||||||
and renders buffered help rather than executing callbacks or reading application
|
and renders buffered help rather than executing callbacks or reading application
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Audit remediation implementation plan
|
# Audit remediation implementation plan
|
||||||
|
|
||||||
Status: in progress; Stages 1–12 complete.
|
Status: in progress; Stages 1–13 complete.
|
||||||
|
|
||||||
Baseline: `d492c7747175c01a53261206c2b4356e53453621`. Before this sprint,
|
Baseline: `d492c7747175c01a53261206c2b4356e53453621`. Before this sprint,
|
||||||
intervening changes recorded decisions and retired the completed older
|
intervening changes recorded decisions and retired the completed older
|
||||||
@@ -442,6 +442,12 @@ Complete F21 when both callers share one adapter and preserve their context.
|
|||||||
|
|
||||||
## Stage 13 — Remove unused parser resource plumbing
|
## Stage 13 — Remove unused parser resource plumbing
|
||||||
|
|
||||||
|
**Complete.** Parser options carry only alias selection, backend output and
|
||||||
|
error destinations explicitly discard, and core retains invocation resources.
|
||||||
|
The test-only App.parse wrapper was removed. Focused routed parsing, help,
|
||||||
|
context, backend parsing and session-isolation tests passed in the offline aligned
|
||||||
|
Go 1.27.0 workspace with `-mod=readonly`. F24 remains open for binding wrappers.
|
||||||
|
|
||||||
Findings: F12 and parsing portion of F24. Sources: `execute.go`, `parser.go`,
|
Findings: F12 and parsing portion of F24. Sources: `execute.go`, `parser.go`,
|
||||||
`internal/commandmeta/metadata.go`, and `internal/cobra/parse.go`.
|
`internal/commandmeta/metadata.go`, and `internal/cobra/parse.go`.
|
||||||
|
|
||||||
|
|||||||
11
execute.go
11
execute.go
@@ -80,10 +80,8 @@ func (a *App) Execute(ctx context.Context, args []string, streams Streams) Outco
|
|||||||
type execution struct {
|
type execution struct {
|
||||||
app *App
|
app *App
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
streams Streams
|
|
||||||
reporter *Reporter
|
reporter *Reporter
|
||||||
debugEvents []string
|
debugEvents []string
|
||||||
options commandmeta.ParseOptions
|
|
||||||
snapshot *routingSnapshot
|
snapshot *routingSnapshot
|
||||||
session *internalcobra.Session
|
session *internalcobra.Session
|
||||||
route routeResult
|
route routeResult
|
||||||
@@ -116,13 +114,10 @@ func (current commandExecution) renderHelpTargets(targets []string) Outcome {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newExecution(app *App, ctx context.Context, streams Streams) *execution {
|
func newExecution(app *App, ctx context.Context, streams Streams) *execution {
|
||||||
streams = normalizeStreams(streams)
|
|
||||||
current := &execution{
|
current := &execution{
|
||||||
app: app,
|
app: app,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
streams: streams,
|
|
||||||
reporter: newReporter(streams),
|
reporter: newReporter(streams),
|
||||||
options: commandmeta.ParseOptions{Context: ctx, Out: streams.Out, Err: streams.Err},
|
|
||||||
}
|
}
|
||||||
if app != nil {
|
if app != nil {
|
||||||
current.snapshot = app.routeSnapshot()
|
current.snapshot = app.routeSnapshot()
|
||||||
@@ -147,7 +142,7 @@ func (current *execution) prepareSession() {
|
|||||||
if current.snapshot == nil {
|
if current.snapshot == nil {
|
||||||
current.snapshot = current.app.routeSnapshot()
|
current.snapshot = current.app.routeSnapshot()
|
||||||
}
|
}
|
||||||
options := current.options
|
options := commandmeta.ParseOptions{}
|
||||||
if current.snapshot != nil {
|
if current.snapshot != nil {
|
||||||
switch current.route.kind {
|
switch current.route.kind {
|
||||||
case routeCanonical:
|
case routeCanonical:
|
||||||
@@ -201,11 +196,11 @@ func (current *execution) renderHelpTargets(targets []string) Outcome {
|
|||||||
// Explicit help targets are paths, not command invocations with operands.
|
// Explicit help targets are paths, not command invocations with operands.
|
||||||
// Discovery routes have no canonical path and retain only trailing options.
|
// Discovery routes have no canonical path and retain only trailing options.
|
||||||
if route.kind == routeUnknown || len(route.args) != len(route.canonicalPath) {
|
if route.kind == routeUnknown || len(route.args) != len(route.canonicalPath) {
|
||||||
session := internalcobra.Prepare(snapshot.metadata, current.options)
|
session := internalcobra.Prepare(snapshot.metadata, commandmeta.ParseOptions{})
|
||||||
view := commandmeta.HelpView{EnteredPath: targets, CanonicalPath: targets}
|
view := commandmeta.HelpView{EnteredPath: targets, CanonicalPath: targets}
|
||||||
return current.app.renderHelpViewOutcomeWithSession(view, current.reporter, session)
|
return current.app.renderHelpViewOutcomeWithSession(view, current.reporter, session)
|
||||||
}
|
}
|
||||||
options := current.options
|
options := commandmeta.ParseOptions{}
|
||||||
switch route.kind {
|
switch route.kind {
|
||||||
case routeCanonical:
|
case routeCanonical:
|
||||||
options.FlagAliases = snapshot.flagAliases[route.canonicalPath.Key()].neutralRecords()
|
options.FlagAliases = snapshot.flagAliases[route.canonicalPath.Key()].neutralRecords()
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ package cobra
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -35,8 +34,7 @@ func Prepare(root *commandmeta.Command, options commandmeta.ParseOptions) *Sessi
|
|||||||
session.metadata = root
|
session.metadata = root
|
||||||
session.flagAliases = append([]commandmeta.FlagAlias(nil), options.FlagAliases...)
|
session.flagAliases = append([]commandmeta.FlagAlias(nil), options.FlagAliases...)
|
||||||
session.flagAliasesForPath = options.FlagAliasesForPath
|
session.flagAliasesForPath = options.FlagAliasesForPath
|
||||||
options = normalizeOptions(options)
|
session.root, session.declarations = buildTree(root)
|
||||||
session.root, session.declarations = buildTree(root, options)
|
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,23 +287,7 @@ func Parse(root *commandmeta.Command, argv []string, options commandmeta.ParseOp
|
|||||||
return Prepare(root, options).Parse(argv)
|
return Prepare(root, options).Parse(argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeOptions(options commandmeta.ParseOptions) commandmeta.ParseOptions {
|
func buildTree(root *commandmeta.Command) (*spfcobra.Command, map[*spfcobra.Command]*commandmeta.Command) {
|
||||||
if options.Context == nil {
|
|
||||||
options.Context = context.Background()
|
|
||||||
}
|
|
||||||
if options.In == nil {
|
|
||||||
options.In = strings.NewReader("")
|
|
||||||
}
|
|
||||||
if options.Out == nil {
|
|
||||||
options.Out = io.Discard
|
|
||||||
}
|
|
||||||
if options.Err == nil {
|
|
||||||
options.Err = io.Discard
|
|
||||||
}
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildTree(root *commandmeta.Command, options commandmeta.ParseOptions) (*spfcobra.Command, map[*spfcobra.Command]*commandmeta.Command) {
|
|
||||||
nodes := make(map[*spfcobra.Command]*commandmeta.Command)
|
nodes := make(map[*spfcobra.Command]*commandmeta.Command)
|
||||||
var build func(*commandmeta.Command) *spfcobra.Command
|
var build func(*commandmeta.Command) *spfcobra.Command
|
||||||
build = func(declaration *commandmeta.Command) *spfcobra.Command {
|
build = func(declaration *commandmeta.Command) *spfcobra.Command {
|
||||||
@@ -321,10 +303,8 @@ func buildTree(root *commandmeta.Command, options commandmeta.ParseOptions) (*sp
|
|||||||
if declaration.Runnable {
|
if declaration.Runnable {
|
||||||
backend.Run = func(*spfcobra.Command, []string) {}
|
backend.Run = func(*spfcobra.Command, []string) {}
|
||||||
}
|
}
|
||||||
backend.SetContext(options.Context)
|
backend.SetOut(io.Discard)
|
||||||
backend.SetIn(options.In)
|
backend.SetErr(io.Discard)
|
||||||
backend.SetOut(options.Out)
|
|
||||||
backend.SetErr(options.Err)
|
|
||||||
backend.SetHelpFunc(func(*spfcobra.Command, []string) {})
|
backend.SetHelpFunc(func(*spfcobra.Command, []string) {})
|
||||||
backend.SetFlagErrorFunc(func(_ *spfcobra.Command, err error) error { return err })
|
backend.SetFlagErrorFunc(func(_ *spfcobra.Command, err error) error { return err })
|
||||||
backend.SetUsageTemplate(usageTemplate)
|
backend.SetUsageTemplate(usageTemplate)
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package cobra
|
package cobra
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -182,7 +180,7 @@ func TestParseReturnsNeutralFailuresAndFreshState(t *testing.T) {
|
|||||||
tree := &Command{Name: "app", Children: []*Command{
|
tree := &Command{Name: "app", Children: []*Command{
|
||||||
{Name: "run", Runnable: true, Flags: []Flag{{Name: "value"}}},
|
{Name: "run", Runnable: true, Flags: []Flag{{Name: "value"}}},
|
||||||
}}
|
}}
|
||||||
options := ParseOptions{Context: context.Background(), In: strings.NewReader("unused"), Out: io.Discard, Err: io.Discard}
|
options := ParseOptions{}
|
||||||
first := Parse(tree, []string{"run", "--value=old"}, options)
|
first := Parse(tree, []string{"run", "--value=old"}, options)
|
||||||
second := Parse(tree, []string{"run"}, options)
|
second := Parse(tree, []string{"run"}, options)
|
||||||
if first.Failure != nil || second.Failure != nil {
|
if first.Failure != nil || second.Failure != nil {
|
||||||
@@ -247,8 +245,7 @@ func TestSelectedPathsResolveThroughBackendParents(t *testing.T) {
|
|||||||
|
|
||||||
func TestSessionReusesTreeForParseHelpAndUsageWithFreshState(t *testing.T) {
|
func TestSessionReusesTreeForParseHelpAndUsageWithFreshState(t *testing.T) {
|
||||||
tree := &Command{Name: "app", Children: []*Command{{Name: "run", Runnable: true, Flags: []Flag{{Name: "value"}}}}}
|
tree := &Command{Name: "app", Children: []*Command{{Name: "run", Runnable: true, Flags: []Flag{{Name: "value"}}}}}
|
||||||
var output, errors bytes.Buffer
|
session := Prepare(tree, ParseOptions{})
|
||||||
session := Prepare(tree, ParseOptions{Context: context.Background(), Out: &output, Err: &errors})
|
|
||||||
first := session.Parse([]string{"run", "--value=old"})
|
first := session.Parse([]string{"run", "--value=old"})
|
||||||
if first.Failure != nil || !reflect.DeepEqual(first.FlagValues, [][]string{{"old"}}) {
|
if first.Failure != nil || !reflect.DeepEqual(first.FlagValues, [][]string{{"old"}}) {
|
||||||
t.Fatalf("first session parse = %#v", first)
|
t.Fatalf("first session parse = %#v", first)
|
||||||
@@ -261,19 +258,15 @@ func TestSessionReusesTreeForParseHelpAndUsageWithFreshState(t *testing.T) {
|
|||||||
if failure != nil || !strings.Contains(line, "run") {
|
if failure != nil || !strings.Contains(line, "run") {
|
||||||
t.Fatalf("session usage = %q, %v", line, failure)
|
t.Fatalf("session usage = %q, %v", line, failure)
|
||||||
}
|
}
|
||||||
if output.Len() != 0 || errors.Len() != 0 {
|
if session.root.OutOrStdout() != io.Discard || session.root.ErrOrStderr() != io.Discard {
|
||||||
t.Fatalf("help escaped session delivery control: out=%q err=%q", output.String(), errors.String())
|
t.Fatal("backend destinations are not discarded")
|
||||||
}
|
}
|
||||||
|
|
||||||
var secondOutput, secondErrors bytes.Buffer
|
second := Prepare(tree, ParseOptions{})
|
||||||
second := Prepare(tree, ParseOptions{Context: context.Background(), Out: &secondOutput, Err: &secondErrors})
|
|
||||||
secondResult := second.Parse([]string{"run"})
|
secondResult := second.Parse([]string{"run"})
|
||||||
if secondResult.Failure != nil || !reflect.DeepEqual(secondResult.FlagValues, [][]string{nil}) {
|
if secondResult.Failure != nil || !reflect.DeepEqual(secondResult.FlagValues, [][]string{nil}) {
|
||||||
t.Fatalf("fresh session state = %#v", secondResult)
|
t.Fatalf("fresh session state = %#v", secondResult)
|
||||||
}
|
}
|
||||||
if output.Len() != 0 || errors.Len() != 0 || secondOutput.Len() != 0 || secondErrors.Len() != 0 {
|
|
||||||
t.Fatal("prepared session streams were unexpectedly used")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMissingSessionTreeReturnsNeutralFailures(t *testing.T) {
|
func TestMissingSessionTreeReturnsNeutralFailures(t *testing.T) {
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package cobra
|
package cobra
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -11,7 +9,7 @@ func BenchmarkPerformanceParse(b *testing.B) {
|
|||||||
wide := performanceFlatTree(128)
|
wide := performanceFlatTree(128)
|
||||||
wideArgs := []string{"wide127"}
|
wideArgs := []string{"wide127"}
|
||||||
deep, deepArgs := performanceNestedTree(24)
|
deep, deepArgs := performanceNestedTree(24)
|
||||||
options := ParseOptions{Context: context.Background(), Out: io.Discard, Err: io.Discard}
|
options := ParseOptions{}
|
||||||
|
|
||||||
for _, benchmark := range []struct {
|
for _, benchmark := range []struct {
|
||||||
name string
|
name string
|
||||||
@@ -41,7 +39,7 @@ func BenchmarkPerformanceParse(b *testing.B) {
|
|||||||
func BenchmarkPerformanceSessionParseAndHelp(b *testing.B) {
|
func BenchmarkPerformanceSessionParseAndHelp(b *testing.B) {
|
||||||
root := performanceFlatTree(8)
|
root := performanceFlatTree(8)
|
||||||
args := []string{"wide007"}
|
args := []string{"wide007"}
|
||||||
session := Prepare(root, ParseOptions{Context: context.Background(), Out: io.Discard, Err: io.Discard})
|
session := Prepare(root, ParseOptions{})
|
||||||
result := session.Parse(args)
|
result := session.Parse(args)
|
||||||
if result.Failure != nil {
|
if result.Failure != nil {
|
||||||
b.Fatalf("fixture parse failed: %s", result.Failure.Message)
|
b.Fatalf("fixture parse failed: %s", result.Failure.Message)
|
||||||
|
|||||||
@@ -2,11 +2,6 @@
|
|||||||
// its command-line backend.
|
// its command-line backend.
|
||||||
package commandmeta
|
package commandmeta
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Key identifies one command inside one routing snapshot.
|
// Key identifies one command inside one routing snapshot.
|
||||||
type Key uint64
|
type Key uint64
|
||||||
|
|
||||||
@@ -181,14 +176,10 @@ type Result struct {
|
|||||||
Failure *Failure
|
Failure *Failure
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseOptions makes parser context and streams explicit.
|
// ParseOptions selects flag aliases for parser contexts.
|
||||||
type ParseOptions struct {
|
type ParseOptions struct {
|
||||||
// FlagAliasesForPath selects aliases for the actual parser context when
|
// FlagAliasesForPath selects aliases for the actual parser context when
|
||||||
// routing has not already selected one. It overrides FlagAliases when set.
|
// routing has not already selected one. It overrides FlagAliases when set.
|
||||||
FlagAliasesForPath func([]string) []FlagAlias
|
FlagAliasesForPath func([]string) []FlagAlias
|
||||||
Context context.Context
|
|
||||||
In io.Reader
|
|
||||||
Out io.Writer
|
|
||||||
Err io.Writer
|
|
||||||
FlagAliases []FlagAlias
|
FlagAliases []FlagAlias
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,10 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
internalcobra "go.maximumdirect.net/commandkit/internal/cobra"
|
|
||||||
commandmeta "go.maximumdirect.net/commandkit/internal/commandmeta"
|
commandmeta "go.maximumdirect.net/commandkit/internal/commandmeta"
|
||||||
"go.maximumdirect.net/commandkit/internal/commandpath"
|
"go.maximumdirect.net/commandkit/internal/commandpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *App) parse(args []string, options commandmeta.ParseOptions) commandmeta.Result {
|
|
||||||
if a == nil {
|
|
||||||
return commandmeta.Result{Failure: &commandmeta.Failure{Message: "cannot parse commands on a nil app"}}
|
|
||||||
}
|
|
||||||
result := internalcobra.Parse(a.routeSnapshot().metadata, args, options)
|
|
||||||
return reduceReportingOccurrences(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func reduceReportingOccurrences(result commandmeta.Result) commandmeta.Result {
|
func reduceReportingOccurrences(result commandmeta.Result) commandmeta.Result {
|
||||||
selection := commandmeta.ReportingSelection{}
|
selection := commandmeta.ReportingSelection{}
|
||||||
for index, occurrence := range result.FlagOccurrences {
|
for index, occurrence := range result.FlagOccurrences {
|
||||||
|
|||||||
@@ -34,15 +34,14 @@ func TestAppParseUsesRegisteredBindingsWithTheAdapter(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
options := commandmeta.ParseOptions{Context: context.Background()}
|
result := parseReportingArgs(app, []string{"config", "show", "one", "--scope", "app"})
|
||||||
result := app.parse([]string{"config", "show", "one", "--scope", "app"}, options)
|
|
||||||
if result.Failure != nil {
|
if result.Failure != nil {
|
||||||
t.Fatalf("parse failed: %s", result.Failure.Message)
|
t.Fatalf("parse failed: %s", result.Failure.Message)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(result.Path, []string{"config", "show"}) || !reflect.DeepEqual(result.FlagValues, [][]string{{"app"}}) || !reflect.DeepEqual(result.Operands, []string{"one"}) {
|
if !reflect.DeepEqual(result.Path, []string{"config", "show"}) || !reflect.DeepEqual(result.FlagValues, [][]string{{"app"}}) || !reflect.DeepEqual(result.Operands, []string{"one"}) {
|
||||||
t.Fatalf("parse result = %#v", result)
|
t.Fatalf("parse result = %#v", result)
|
||||||
}
|
}
|
||||||
result = app.parse([]string{"config", "--scope", "app", "show"}, options)
|
result = parseReportingArgs(app, []string{"config", "--scope", "app", "show"})
|
||||||
if result.Failure == nil {
|
if result.Failure == nil {
|
||||||
t.Fatal("accepted a child flag before the complete command path")
|
t.Fatal("accepted a child flag before the complete command path")
|
||||||
}
|
}
|
||||||
@@ -55,7 +54,7 @@ func TestRoutingSnapshotPairsParsedKeyWithItsRunner(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
snapshot := app.routeSnapshot()
|
snapshot := app.routeSnapshot()
|
||||||
session := internalcobra.Prepare(snapshot.metadata, commandmeta.ParseOptions{Context: context.Background()})
|
session := internalcobra.Prepare(snapshot.metadata, commandmeta.ParseOptions{})
|
||||||
parsed := session.Parse([]string{"run"})
|
parsed := session.Parse([]string{"run"})
|
||||||
if parsed.Failure != nil {
|
if parsed.Failure != nil {
|
||||||
t.Fatalf("parse failed: %s", parsed.Failure.Message)
|
t.Fatalf("parse failed: %s", parsed.Failure.Message)
|
||||||
|
|||||||
Reference in New Issue
Block a user