Skip to content

Commit

Permalink
For #615 - Implement formatting by reflection
Browse files Browse the repository at this point in the history
  • Loading branch information
Lars T Hansen committed Oct 29, 2024
1 parent c8b7d4f commit b18dfc8
Show file tree
Hide file tree
Showing 36 changed files with 1,601 additions and 953 deletions.
30 changes: 30 additions & 0 deletions code/sonalyze/VOCAB.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Data field vocabulary

(This is evolving.)

Field naming is pretty arbitrary and it is not going to be cleaned up right now. For the most part
we can fix things over time through the use of aliases.

"Old" names such as "rcpu", "rmem" should probably not be used more than absolutely necessary,
ideally all new names are fairly self-explanatory and not very abbreviated.

Contextuality is important to make things hang together. The precise meaning of the field must be
derivable from name + context + type + documentation, ideally from name + context + documentation
since the user may not have access to the type. Name + documentation must be visible from -fmt
help, and context is given by the verb. (Hence plain "Name" in the cluster info is not as bad as it
looks because it is plain from context and documentation that we're talking about the cluster name;
"Clustername" might have been better, but not massively much better.)

Spelling standards that we should follow when we have a chance to (re)name a field:

* Cpu, Cpus not CPU, CPUS, CPUs
* Gpu, Gpus not GPU, GPUS, GPUs
* GB not GiB, the unit is 2^30
* MB not MiB, the unit is 2^20
* KB not KiB, the unit is 2^10
* JobId not JobID
* Units on fields that can have multiple natural units, eg, ResidentMemGB not ResidentMem

(And yet there may be other considerations. The sacct table names such as UsedCPU and MaxRSS are
currently the way they are because those are the names adopted by sacct. But on the whole it'd
probably be better to follow our own naming standards and explain the mapping in the documentation.)
57 changes: 24 additions & 33 deletions code/sonalyze/clusters/clusters.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import (
"errors"
"flag"
"io"
"reflect"
"slices"
"strings"

umaps "go-utils/maps"
"go-utils/options"
uslices "go-utils/slices"

. "sonalyze/command"
. "sonalyze/common"
Expand Down Expand Up @@ -93,18 +95,19 @@ func (cc *ClusterCommand) Clusters(_ io.Reader, stdout, stderr io.Writer) error
return err
}

printable := umaps.Values(clusters)
slices.SortFunc(printable, func(a, b *db.ClusterEntry) int {
sortable := umaps.Values(clusters)
slices.SortFunc(sortable, func(a, b *db.ClusterEntry) int {
return cmp.Compare(a.Name, b.Name)
})
printable := uslices.Map(sortable, func(x *db.ClusterEntry) any { return x })

FormatData(
stdout,
cc.FormatArgs.PrintFields,
cc.PrintFields,
clustersFormatters,
cc.FormatArgs.PrintOpts,
cc.PrintOpts,
printable,
false,
ComputePrintMods(cc.PrintOpts),
)

return nil
Expand All @@ -125,38 +128,26 @@ nodes
Output records are sorted by cluster name. The default format is 'fixed'.
`

type clustersCtx = bool
type clustersSummary = db.ClusterEntry

const clustersDefaultFields = "cluster,aliases,desc"
const v0ClustersDefaultFields = "cluster,aliases,desc"
const v1ClustersDefaultFields = "Name,Aliases,Description"
const clustersDefaultFields = v0ClustersDefaultFields

// MT: Constant after initialization; immutable
var clustersAliases = map[string][]string{
"default": strings.Split(clustersDefaultFields, ","),
"description": []string{"desc"},
"default": strings.Split(clustersDefaultFields, ","),
"v0default": strings.Split(v0ClustersDefaultFields, ","),
"v1default": strings.Split(v1ClustersDefaultFields, ","),
}

type SFS = SimpleFormatSpec

// MT: Constant after initialization; immutable
var clustersFormatters = map[string]Formatter[*clustersSummary, clustersCtx]{
"cluster": {
func(i *clustersSummary, _ clustersCtx) string {
return i.Name
},
"Cluster name",
},
"desc": {
func(i *clustersSummary, _ clustersCtx) string {
return i.Description
},
"Human-consumable cluster summary",
var clustersFormatters map[string]Formatter[any, PrintMods] = ReflectFormattersFromMap(
// TODO: Go 1.22, reflect.TypeFor[db.ClusterEntry]
reflect.TypeOf((*db.ClusterEntry)(nil)).Elem(),
map[string]any{
"Name": SFS{"Cluster name", "cluster"},
"Description": SFS{"Human-consumable cluster summary", "desc"},
"Aliases": SFS{"Aliases of cluster", "aliases"},
},
"aliases": {
func(i *clustersSummary, _ clustersCtx) string {
// Print aliases in deterministic order
aliases := slices.Clone(i.Aliases)
slices.Sort(aliases)
return strings.Join(aliases, ",")
},
"Aliases of cluster",
},
}
)
2 changes: 1 addition & 1 deletion code/sonalyze/command/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func EnsureConfigForInputStreams(
// Remove streams for which we have no config data.
bad := make(map[sonarlog.InputStreamKey]bool)
for key, stream := range streams {
hn := (*stream)[0].S.Host.String()
hn := (*stream)[0].Host.String()
if cfg.LookupHost(hn) == nil {
bad[key] = true
Log.Infof("Warning: Missing host configuration for %s", hn)
Expand Down
8 changes: 4 additions & 4 deletions code/sonalyze/command/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"strings"
"unicode/utf8"

"go-utils/maps"
umaps "go-utils/maps"
)

type FormatOptions struct {
Expand All @@ -34,9 +34,9 @@ func (fo *FormatOptions) IsDefaultFormat() bool {
// strings found in `spec`, plus also "help" if fmtOpt=="help". Expand aliases (though not
// recursively: aliases must map to fundamental names).

func ParseFormatSpec[Data, Ctx any](
func ParseFormatSpec[T any](
defaults, fmtOpt string,
formatters map[string]Formatter[Data, Ctx],
formatters map[string]T,
aliases map[string][]string,
) ([]string, map[string]bool, error) {
spec := fmtOpt
Expand Down Expand Up @@ -484,7 +484,7 @@ func PrintFormatHelp(out io.Writer, h *FormatHelp) {
}
if len(h.Aliases) > 0 {
fmt.Fprintln(out, "\nAliases:")
aliases := maps.Keys(h.Aliases)
aliases := umaps.Keys(h.Aliases)
sort.Sort(sort.StringSlice(aliases))
for _, k := range aliases {
// Do not sort the names in the expansion because the order matters
Expand Down
Loading

0 comments on commit b18dfc8

Please sign in to comment.