import argparse import os import re from jinja2 import Environment, FileSystemLoader def make_pythonic_variable_name(input_string): variable_name = input_string.strip() variable_name = re.sub(r'\W|^(?=\d)', '_', variable_name) if not variable_name[0].isalpha() and variable_name[0] != '_': variable_name = f'_{variable_name}' return variable_name def convert_tool_name_to_class_name(tool_name): return ''.join(word.title() for word in tool_name.split('_')) def create_file(path): with open(path, 'w'): pass def create_folder(path): os.makedirs(path, exist_ok=True) def create_tool_project_structure(destination: str, package_name: str, tool_name: str, function_name: str, is_class_way=False): if is_class_way: class_name = convert_tool_name_to_class_name(tool_name) # Load templates templates_abs_path = os.path.join(os.path.dirname(__file__), "templates") file_loader = FileSystemLoader(templates_abs_path) env = Environment(loader=file_loader) # Create new directory if os.path.exists(destination): print("Destination already exists. Please choose another one.") return os.makedirs(destination, exist_ok=True) # Generate setup.py template = env.get_template('setup.py.j2') output = template.render(package_name=package_name, tool_name=tool_name) with open(os.path.join(destination, 'setup.py'), 'w') as f: f.write(output) # Generate MANIFEST.in template = env.get_template('MANIFEST.in.j2') output = template.render(package_name=package_name) with open(os.path.join(destination, 'MANIFEST.in'), 'w') as f: f.write(output) # Create tools folder and __init__.py, tool.py inside it tools_dir = os.path.join(destination, package_name, 'tools') create_folder(tools_dir) create_file(os.path.join(tools_dir, '__init__.py')) with open(os.path.join(tools_dir, '__init__.py'), 'w') as f: f.write('__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore\n') # Generate tool.py if is_class_way: template = env.get_template('tool2.py.j2') output = template.render(class_name=class_name, function_name=function_name) else: template = env.get_template('tool.py.j2') output = template.render(function_name=function_name) with open(os.path.join(tools_dir, f'{tool_name}.py'), 'w') as f: f.write(output) # Generate utils.py template = env.get_template('utils.py.j2') output = template.render() with open(os.path.join(tools_dir, 'utils.py'), 'w') as f: f.write(output) create_file(os.path.join(destination, package_name, '__init__.py')) with open(os.path.join(destination, package_name, '__init__.py'), 'w') as f: f.write('__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore\n') # Create yamls folder and __init__.py inside it yamls_dir = os.path.join(destination, package_name, 'yamls') create_folder(yamls_dir) # Create tool yaml if is_class_way: template = env.get_template('tool2.yaml.j2') output = template.render(package_name=package_name, tool_name=tool_name, class_name=class_name, function_name=function_name) else: template = env.get_template('tool.yaml.j2') output = template.render(package_name=package_name, tool_name=tool_name, function_name=function_name) with open(os.path.join(yamls_dir, f'{tool_name}.yaml'), 'w') as f: f.write(output) # Create test folder and __init__.py inside it tests_dir = os.path.join(destination, 'tests') create_folder(tests_dir) create_file(os.path.join(tests_dir, '__init__.py')) # Create test_tool.py if is_class_way: template = env.get_template('test_tool2.py.j2') output = template.render(package_name=package_name, tool_name=tool_name, class_name=class_name, function_name=function_name) else: template = env.get_template('test_tool.py.j2') output = template.render(package_name=package_name, tool_name=tool_name, function_name=function_name) with open(os.path.join(tests_dir, f'test_{tool_name}.py'), 'w') as f: f.write(output) print(f'Generated tool package template for {package_name} at {destination}') if __name__ == "__main__": parser = argparse.ArgumentParser(description="promptflow tool template generation arguments.") parser.add_argument("--package-name", "-p", type=str, help="your tool package's name", required=True) parser.add_argument("--destination", "-d", type=str, help="target folder you want to place the generated template", required=True) parser.add_argument("--tool-name", "-t", type=str, help="your tool's name, by default is hello_world_tool", required=False) parser.add_argument("--function-name", "-f", type=str, help="your tool's function name, by default is your tool's name", required=False) parser.add_argument("--use-class", action='store_true', help="Specify whether to use a class implementation way.") args = parser.parse_args() destination = args.destination package_name = make_pythonic_variable_name(args.package_name) package_name = package_name.lower() if args.tool_name: tool_name = make_pythonic_variable_name(args.tool_name) else: tool_name = 'hello_world_tool' tool_name = tool_name.lower() if args.function_name: function_name = make_pythonic_variable_name(args.function_name) else: function_name = tool_name function_name = function_name.lower() create_tool_project_structure(destination, package_name, tool_name, function_name, args.use_class)