ff1c0b58dd
This adds what I think needs to be done to support categories for flags but we will see if that works. It also forces the scripts to use python2 since they blow up under python3 which is becoming the default python on many linux systems. Small fix to app_test as well so it conforms to the new Flag interface.
263 lines
8.2 KiB
Python
Executable File
263 lines
8.2 KiB
Python
Executable File
#!/usr/bin/env python2
|
|
"""
|
|
The flag types that ship with the cli library have many things in common, and
|
|
so we can take advantage of the `go generate` command to create much of the
|
|
source code from a list of definitions. These definitions attempt to cover
|
|
the parts that vary between flag types, and should evolve as needed.
|
|
|
|
An example of the minimum definition needed is:
|
|
|
|
{
|
|
"name": "SomeType",
|
|
"type": "sometype",
|
|
"context_default": "nil"
|
|
}
|
|
|
|
In this example, the code generated for the `cli` package will include a type
|
|
named `SomeTypeFlag` that is expected to wrap a value of type `sometype`.
|
|
Fetching values by name via `*cli.Context` will default to a value of `nil`.
|
|
|
|
A more complete, albeit somewhat redundant, example showing all available
|
|
definition keys is:
|
|
|
|
{
|
|
"name": "VeryMuchType",
|
|
"type": "*VeryMuchType",
|
|
"value": true,
|
|
"dest": false,
|
|
"doctail": " which really only wraps a []float64, oh well!",
|
|
"context_type": "[]float64",
|
|
"context_default": "nil",
|
|
"parser": "parseVeryMuchType(f.Value.String())",
|
|
"parser_cast": "[]float64(parsed)"
|
|
}
|
|
|
|
The meaning of each field is as follows:
|
|
|
|
name (string) - The type "name", which will be suffixed with
|
|
`Flag` when generating the type definition
|
|
for `cli` and the wrapper type for `altsrc`
|
|
type (string) - The type that the generated `Flag` type for `cli`
|
|
is expected to "contain" as its `.Value` member
|
|
value (bool) - Should the generated `cli` type have a `Value`
|
|
member?
|
|
dest (bool) - Should the generated `cli` type support a
|
|
destination pointer?
|
|
doctail (string) - Additional docs for the `cli` flag type comment
|
|
context_type (string) - The literal type used in the `*cli.Context`
|
|
reader func signature
|
|
context_default (string) - The literal value used as the default by the
|
|
`*cli.Context` reader funcs when no value is
|
|
present
|
|
parser (string) - Literal code used to parse the flag `f`,
|
|
expected to have a return signature of
|
|
(value, error)
|
|
parser_cast (string) - Literal code used to cast the `parsed` value
|
|
returned from the `parser` code
|
|
"""
|
|
|
|
from __future__ import print_function, unicode_literals
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import textwrap
|
|
|
|
|
|
class _FancyFormatter(argparse.ArgumentDefaultsHelpFormatter,
|
|
argparse.RawDescriptionHelpFormatter):
|
|
pass
|
|
|
|
|
|
def main(sysargs=sys.argv[:]):
|
|
parser = argparse.ArgumentParser(
|
|
description='Generate flag type code!',
|
|
formatter_class=_FancyFormatter)
|
|
parser.add_argument(
|
|
'package',
|
|
type=str, default='cli', choices=_WRITEFUNCS.keys(),
|
|
help='Package for which flag types will be generated'
|
|
)
|
|
parser.add_argument(
|
|
'-i', '--in-json',
|
|
type=argparse.FileType('r'),
|
|
default=sys.stdin,
|
|
help='Input JSON file which defines each type to be generated'
|
|
)
|
|
parser.add_argument(
|
|
'-o', '--out-go',
|
|
type=argparse.FileType('w'),
|
|
default=sys.stdout,
|
|
help='Output file/stream to which generated source will be written'
|
|
)
|
|
parser.epilog = __doc__
|
|
|
|
args = parser.parse_args(sysargs[1:])
|
|
_generate_flag_types(_WRITEFUNCS[args.package], args.out_go, args.in_json)
|
|
return 0
|
|
|
|
|
|
def _generate_flag_types(writefunc, output_go, input_json):
|
|
types = json.load(input_json)
|
|
|
|
tmp = tempfile.NamedTemporaryFile(suffix='.go', delete=False)
|
|
writefunc(tmp, types)
|
|
tmp.close()
|
|
|
|
new_content = subprocess.check_output(
|
|
['goimports', tmp.name]
|
|
).decode('utf-8')
|
|
|
|
print(new_content, file=output_go, end='')
|
|
output_go.flush()
|
|
os.remove(tmp.name)
|
|
|
|
|
|
def _set_typedef_defaults(typedef):
|
|
typedef.setdefault('doctail', '')
|
|
typedef.setdefault('context_type', typedef['type'])
|
|
typedef.setdefault('dest', True)
|
|
typedef.setdefault('value', True)
|
|
typedef.setdefault('parser', 'f.Value, error(nil)')
|
|
typedef.setdefault('parser_cast', 'parsed')
|
|
|
|
|
|
def _write_cli_flag_types(outfile, types):
|
|
_fwrite(outfile, """\
|
|
package cli
|
|
|
|
// WARNING: This file is generated!
|
|
|
|
""")
|
|
|
|
for typedef in types:
|
|
_set_typedef_defaults(typedef)
|
|
|
|
_fwrite(outfile, """\
|
|
// {name}Flag is a flag with type {type}{doctail}
|
|
type {name}Flag struct {{
|
|
Name string
|
|
Usage string
|
|
EnvVar string
|
|
FilePath string
|
|
Hidden bool
|
|
Category string
|
|
""".format(**typedef))
|
|
|
|
if typedef['value']:
|
|
_fwrite(outfile, """\
|
|
Value {type}
|
|
""".format(**typedef))
|
|
|
|
if typedef['dest']:
|
|
_fwrite(outfile, """\
|
|
Destination *{type}
|
|
""".format(**typedef))
|
|
|
|
_fwrite(outfile, "\n}\n\n")
|
|
|
|
_fwrite(outfile, """\
|
|
// String returns a readable representation of this value
|
|
// (for usage defaults)
|
|
func (f {name}Flag) String() string {{
|
|
return FlagStringer(f)
|
|
}}
|
|
|
|
// GetName returns the name of the flag
|
|
func (f {name}Flag) GetName() string {{
|
|
return f.Name
|
|
}}
|
|
|
|
// GetHidden lets us know if the flag is hidden or not
|
|
func (f {name}Flag) GetHidden() bool {{
|
|
return f.Hidden
|
|
}}
|
|
|
|
// {name} looks up the value of a local {name}Flag, returns
|
|
// {context_default} if not found
|
|
func (c *Context) {name}(name string) {context_type} {{
|
|
return lookup{name}(name, c.flagSet)
|
|
}}
|
|
|
|
// Global{name} looks up the value of a global {name}Flag, returns
|
|
// {context_default} if not found
|
|
func (c *Context) Global{name}(name string) {context_type} {{
|
|
if fs := lookupGlobalFlagSet(name, c); fs != nil {{
|
|
return lookup{name}(name, fs)
|
|
}}
|
|
return {context_default}
|
|
}}
|
|
|
|
func lookup{name}(name string, set *flag.FlagSet) {context_type} {{
|
|
f := set.Lookup(name)
|
|
if f != nil {{
|
|
parsed, err := {parser}
|
|
if err != nil {{
|
|
return {context_default}
|
|
}}
|
|
return {parser_cast}
|
|
}}
|
|
return {context_default}
|
|
}}
|
|
""".format(**typedef))
|
|
|
|
|
|
def _write_altsrc_flag_types(outfile, types):
|
|
_fwrite(outfile, """\
|
|
package altsrc
|
|
|
|
import (
|
|
"gopkg.in/urfave/cli.v1"
|
|
)
|
|
|
|
// WARNING: This file is generated!
|
|
|
|
""")
|
|
|
|
for typedef in types:
|
|
_set_typedef_defaults(typedef)
|
|
|
|
_fwrite(outfile, """\
|
|
// {name}Flag is the flag type that wraps cli.{name}Flag to allow
|
|
// for other values to be specified
|
|
type {name}Flag struct {{
|
|
cli.{name}Flag
|
|
set *flag.FlagSet
|
|
}}
|
|
|
|
// New{name}Flag creates a new {name}Flag
|
|
func New{name}Flag(fl cli.{name}Flag) *{name}Flag {{
|
|
return &{name}Flag{{{name}Flag: fl, set: nil}}
|
|
}}
|
|
|
|
// Apply saves the flagSet for later usage calls, then calls the
|
|
// wrapped {name}Flag.Apply
|
|
func (f *{name}Flag) Apply(set *flag.FlagSet) {{
|
|
f.set = set
|
|
f.{name}Flag.Apply(set)
|
|
}}
|
|
|
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
|
// wrapped {name}Flag.ApplyWithError
|
|
func (f *{name}Flag) ApplyWithError(set *flag.FlagSet) error {{
|
|
f.set = set
|
|
return f.{name}Flag.ApplyWithError(set)
|
|
}}
|
|
""".format(**typedef))
|
|
|
|
|
|
def _fwrite(outfile, text):
|
|
print(textwrap.dedent(text), end='', file=outfile)
|
|
|
|
|
|
_WRITEFUNCS = {
|
|
'cli': _write_cli_flag_types,
|
|
'altsrc': _write_altsrc_flag_types
|
|
}
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|