[EDIT]
I would like to create dynamic command / argument structure from a custom python tree object.
Referencing this post I managed to properly bind the commands:
Stackoverflow: Dynamically Create click commands
The goal would be to use the bash shell to feed a command of arbitrary depth, and have the cli tab completion dynamically populated with the next level within the hierarchy along with help for their arguments ( fed in through a dictionary resolver_dict
)
honeycomb content
would run the content
but:
honeycomb content asset
Would run the content asset
block, but not both content
then asset
I am taking care of all the argument inheritance within the honeycomb
object, so I don't believe I need to forward arguments via click for this as suggested in other posts.
Share options between click commands
So, if the content
block has an argument called studio_root
, and the content asset
has an argument called asset_name
, then the I am internally forwarding ALL arguments from the parents to all their children. ie content asset
will have both the studio_root
and asset_name
arguments already present via the resolver_dict
. ( see below )
content = {'studio_root': some_value}
asset = {'studio_root': some_value (inherited), 'asset_name': lulu}
I am closer. I am able to create / load commands & arguments with the following yaml structure:
Yaml Structure:
_content:
studio_root: /studio
_asset:
asset_name: larry_boo
but with the following command:
honeycomb content asset --asset_name lulu
I am getting the following error:
$ honeycomb content asset --asset_name lulu
I am the "<content>" command
Usage: honeycomb content asset [OPTIONS] COMMAND [ARGS]...
Try 'honeycomb content asset --help' for help.
Error: Missing command.
There are 2 problems here.
- We can think of the command
honeycomb content asset
as a path to a single command... not a chain of commands. I only want the final path to run: ie -honeycomb content asset
should ONLY run theasset
command and not thehoneycomb content
block. Since the click groups are nested, this might require a higher level control over the command invoking? In this case, It appears that it is first running (honeycomb content
) and then failing on thehoneycomb content asset
command? - I assume the "Missing command" is the
honeycomb content asset
command, but that should be:print('I am the "<asset>" command')
, which is missing?
Here is the code:
DATA_PATH = '{}/configs/hive.yaml'.format(pathlib.Path(__file__).parent.parent)
@click.group()
@click.pass_context
def cli(ctx):
pass
# bind commands and args
def bind_func(name, c, kwargs):
def func(**kwargs):
click.echo('I am the "<{}>" command'.format(name))
if not kwargs:
print('found no kwargs')
return(func)
# add all the key, values in node_data
for key, value in kwargs.items():
if key.startswith('__'):
continue
if key == 'command':
continue
#click.echo('\tadding option: {}'.format(key))
option = click.option('--{}'.format(key, default=value))
func = option(func)
# rename the command and return it
func.__name__ = name
return func
def main():
'''
'''
# Load the honeycomb object ( tree of nodes )
hcmb = hcm.Honeycomb(DATA_PATH)
# get the buildable nodes
nodes = hcmb.builder.get_buildable_nodes()
# determine the root path
cli_groups = {'|cli': cli}
for node in nodes:
nice_name = node.get_name()[1:]
path = node.get_path()
cli_path = '|cli' + path
parent = node.get_parent()
if not parent:
parent_cli_path = '|cli'
else:
parent_cli_path = '|cli' + parent.get_path()
# get the node data to add the arguments:
resolver_dict = node.get_resolver_dict()
# create the new group in the appropriate parent group
func = bind_func(nice_name, '_f', resolver_dict)
new_group = cli_groups[parent_cli_path].group(name=nice_name)(func)
# and append it to the list of groups
cli_groups[cli_path] = new_group
# now call the cli
cli()
if __name__ == '__main__':
main()
Sorry for the long code details, but I think it would be difficult to understand what I am doing without it. Maybe I am trying to force click to work in ways it wasn't intended?