Closed xmnlab closed 8 months ago
@abhijeetSaroha if you create a script with this code (for example, testtyper.py), you can test it locally using this:
$ python testtyper.py --help
this example still need changes in order to recognize well the args for each target
this is an example how to have it working also with the args:
import typer
import click
app = typer.Typer()
# Example targets dictionary
targets = {
"clean.all": {
"help": "clean all temporary files"
},
"tests.unit": {
"help": "unit tests",
"args": {
"testname": {
"type": "str",
"default": "tests",
"help": "set the file name of the test file."
}
}
},
"tests.linter": {
"help": "run linter",
},
}
def type_mapper(type_name):
"""
Maps a string representation of a type to the actual Python type.
Parameters
----------
type_name : str
The string representation of the type.
Returns
-------
type
The corresponding Python type.
"""
type_mapping = {
'str': str,
'int': int,
'float': float,
'bool': bool
# Add more mappings as needed
}
return type_mapping.get(type_name, str)
def apply_click_options(command_function, options):
"""
Apply Click options to a Typer command function.
Parameters
----------
command_function : callable
The Typer command function to which options will be applied.
options : dict
A dictionary of options to apply.
Returns
-------
callable
The command function with options applied.
"""
for opt_name, opt_details in options.items():
click_option = click.option(
f'--{opt_name}',
default=opt_details.get('default'),
type=type_mapper(opt_details.get('type', 'str')),
help=opt_details.get('help', '')
)
print()
command_function = click_option(command_function)
return command_function
def create_dynamic_command(name, args):
"""
Dynamically create a Typer command with the specified options.
Parameters
----------
name : str
The command name.
args : dict
The command arguments and options.
"""
args_str = "" if not args.get('args', {}) else ",".join([
f"{name}: {spec['type']}" + ('' if not spec.get('default') else f'= \"{spec["default"]}\"')
for name, spec in args.get('args', {}).items()
])
decorator = app.command(
name=name,
help=args['help']
)
function_code = (
f"def dynamic_command({args_str}):\n"
" typer.echo(f'Executing ' + name)\n"
"\n"
)
local_vars = {}
exec(function_code, globals(), local_vars)
dynamic_command = decorator(local_vars["dynamic_command"])
# Apply Click options to the Typer command
if 'args' in args:
dynamic_command = apply_click_options(dynamic_command, args['args'])
return dynamic_command
# Add dynamically created commands to Typer app
for name, args in targets.items():
create_dynamic_command(name, args)
if __name__ == "__main__":
app()
it is a very dirty example, for example for default it is just working for strings, but it should work for other types as well it would need a lot of refactoring to have it properly working ..
def create_args_string(args):
args_rendered = []
for name, spec in args.get('args', {}).items():
arg_str = f"{name}: {spec['type']}"
if not spec.get('default'):
args_rendered.append(arg_str)
continue
if spec['type'] == "str":
arg_str += f'= \"{spec["default"]}\"'
else:
arg_str += f'= {spec["default"]}'
args_rendered.append(arg_str)
return "".join(args_rendered)
resolved by #82
initial idea about how it could be implemented:
this is not the final version of the code, instead it is an example that shows that it would be possible