#!/usr/bin/env python3 from __future__ import print_function import argparse import os import re import sys def trim(s): """ All repetition of any kind of space is replaced by a single space and remove trailing space at beginning or end. """ # regex to replace all space groups by a single space # use split() to remove trailing space at beginning/end return re.sub('\s+', ' ', s).strip() def quotesForStrings(valueStr): """ Return the input string with quotes if it cannot be cast into another builtin type. """ v = valueStr try: int(valueStr) except ValueError: try: float(valueStr) except ValueError: if "'" in valueStr: v = "'''{}'''".format(valueStr) else: v = "'{}'".format(valueStr) return v def convertToLabel(name): camelCaseToLabel = re.sub('()([A-Z][a-z]*?)', r'\1 \2', name) snakeToLabel = ' '.join(word.capitalize() for word in camelCaseToLabel.split('_')) snakeToLabel = ' '.join(word.capitalize() for word in snakeToLabel.split(' ')) return snakeToLabel parser = argparse.ArgumentParser(description='Create a new Node Type') parser.add_argument('node', metavar='NodeName', type=str, help='New node name') parser.add_argument('--output', metavar='DIR', type=str, default=os.path.dirname(__file__), help='Output plugin folder') parser.add_argument('--parser', metavar='PARSER', type=str, default='boost', help='Select the parser adapted for your command line: {boost,cmdLineLib,basic}.') parser.add_argument("--force", help="Allows to ovewrite the output plugin file.", action="store_true") args = parser.parse_args() if sys.stdin.isatty(): print('No input documentation.') print('Usage: YOUR_COMMAND --help | {cmd} YourCommand'.format(cmd=os.path.splitext(__file__)[0])) exit(-1) inputCmdLineDoc = ''.join([line for line in sys.stdin]) outputNodeStr = ''' from meshroom.processGraph import desc class __COMMANDNAME__(desc.CommandLineNode): internalFolder = '{cache}/{nodeType}/{uid0}/' commandLine = '{nodeType} {allParams}' '''.replace('__COMMANDNAME__', args.node) print(inputCmdLineDoc) args_re = None if args.parser == 'boost': args_re = re.compile( '^\s+' # space(s) '\[?\s*' # potential '[' '(?:-(?P\w+)\|?)?' # potential argument short name '\s*\[?' # potential '[' '\s*--(?P\w+)' # argument long name '(?:\s*\])?' # potential ']' '(?:\s+(?P\w+)?)?' # potential arg '(?:\s+\(\=(?P\w+)\))?' # potential default value '\s+(?P.*?)\n' # end of the line '(?P(?:\s+[^-\s].+?\n)*)' # next documentation lines , re.MULTILINE) elif args.parser == 'cmdLineLib': args_re = re.compile( '^' '\[' # '[' '-(?P\w+)' # argument short name '\|' '--(?P\w+)' # argument long name '(?:\s+(?P\w+)?)?' # potential arg '\]' # ']' '()' # no default value '(?P.*?)?\n' # end of the line '(?P(?:[^\[\w].+?\n)*)' # next documentation lines , re.MULTILINE) elif args.parser == 'basic': args_re = re.compile('()--(?P\w+)()()()()') else: print('Error: Unknown input parser "{}"'.format(args.parser)) exit(-1) inputArgs = args_re.findall(inputCmdLineDoc) print('='*80) for inputArg in inputArgs: shortName = inputArg[0] longName = inputArg[1] if longName == 'help': continue # skip help argument arg = inputArg[2] value = inputArg[3] description = trim(''.join(inputArg[4:])) inputArgLower = ' '.join(inputArg).lower() isFile = 'path' in inputArgLower or 'folder' in inputArgLower or 'file' in inputArgLower isOutput = 'output' in inputArgLower outputNodeStr += """ {name} = desc.{attributeType}( label='{label}', description='''{description}''', value={value}, shortName='{shortName}', arg='{arg}', uid=[0], isOutput={isOutput}, )""".format( name=longName, attributeType='FileAttribute' if isFile else 'ParamAttribute', label=convertToLabel(longName), description=description, value=quotesForStrings(value), shortName=shortName, arg=arg, isOutput=isOutput, ) outputFilepath = os.path.join(args.output, args.node + '.py') if not args.force and os.path.exists(outputFilepath): print('Plugin "{}" already exists "{}".'.format(args.node, outputFilepath)) exit(-1) with open(outputFilepath, 'w') as pluginFile: pluginFile.write(outputNodeStr) print('New node exported to: "{}"'.format(outputFilepath))