[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 assetas a path to a single command... not a chain of commands. I only want the final path to run: ie -honeycomb content assetshould ONLY run theassetcommand and not thehoneycomb contentblock. 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 assetcommand? - I assume the "Missing command" is the
honeycomb content assetcommand, 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?