def __init__(self, configFile): loadPrcFile(configFile) self.ignoreTypes = { '': ConfigVariableBool('ignore-client', True).getValue(), 'AI': ConfigVariableBool('ignore-AI', False).getValue(), 'UD': ConfigVariableBool('ignore-UD', False).getValue(), 'OV': ConfigVariableBool('ignore-OV', True).getValue() } self.wantOverwrite = ConfigVariableBool('overwrite-files', False).getValue() self.generateNonImportDclasses = ConfigVariableBool('generate-non-import-dclasses', False).getValue() self.wantInit = ConfigVariableBool('generate-init', False).getValue() self.dcfile = DCFile() self.dcfile.readAll() self.classesTuples = [] self.dclass2module = {} self.className2Fields = {} self.className2ImportSymbol = {} self.dclass2subclass = {} self.ignoredClasses = [] if not self.dcfile.allObjectsValid(): print 'There was an error reading the dcfile!' return self.readImports() if self.generateNonImportDclasses: self.readDclasses() if not os.path.isdir('output'): os.makedirs('./output') os.chdir('output') else: os.chdir('output') self.readClasses() self.readFields() print 'Done.'
class DCStubGenerator: def __init__(self, configFile): loadPrcFile(configFile) self.ignoreTypes = { '': ConfigVariableBool('ignore-client', True).getValue(), 'AI': ConfigVariableBool('ignore-AI', False).getValue(), 'UD': ConfigVariableBool('ignore-UD', False).getValue(), 'OV': ConfigVariableBool('ignore-OV', True).getValue() } self.wantOverwrite = ConfigVariableBool('overwrite-files', False).getValue() self.generateNonImportDclasses = ConfigVariableBool('generate-non-import-dclasses', False).getValue() self.wantInit = ConfigVariableBool('generate-init', False).getValue() self.dcfile = DCFile() self.dcfile.readAll() self.classesTuples = [] self.dclass2module = {} self.className2Fields = {} self.className2ImportSymbol = {} self.dclass2subclass = {} self.ignoredClasses = [] if not self.dcfile.allObjectsValid(): print 'There was an error reading the dcfile!' return self.readImports() if self.generateNonImportDclasses: self.readDclasses() if not os.path.isdir('output'): os.makedirs('./output') os.chdir('output') else: os.chdir('output') self.readClasses() self.readFields() print 'Done.' def readImports(self): for i in xrange(self.dcfile.getNumImportModules()): importModule = self.dcfile.getImportModule(i) isImportClass = False if '/' in importModule: isImportClass = importModule modules = [e+'.' for e in importModule.split('.')[:-1] if e != ""] importModule = ''.join(modules)[:-1] for n in xrange(self.dcfile.getNumImportSymbols(i)): symbol = self.dcfile.getImportSymbol(i, n) classes = symbol.split('/') subclasses = [] if symbol == '*': print 'Skipping import for %s. Can\'t create classes for wildcard imports!' % importModule continue if isImportClass: subclass = isImportClass.split('.')[-1] subclasses = subclass.split('/') if len(subclasses) > 1: for dcClass in subclasses[1:]: subclasses[subclasses.index(dcClass)] = subclasses[0] + dcClass if len(classes) > 1: for dcClass in classes[1:]: classes[classes.index(dcClass)] = classes[0] + dcClass if isImportClass: for dcClass in classes: self.dclass2subclass[dcClass] = subclasses[classes.index(dcClass)] self.classesTuples.append(classes) for dcClass in classes: self.dclass2module[dcClass] = importModule if importModule.split('.')[0] in ('direct', 'panda3d'): continue self.validateModule(importModule) def readClasses(self): for classes in self.classesTuples: for dcClass in classes: importModule = self.dclass2module.get(dcClass) if not importModule: importLine = 'import %s' % (dcClass) else: importLine = 'from %s import %s' % (importModule, dcClass) if importModule.split('.')[0] in ('direct', 'panda3d'): continue generated = False try: exec importLine except Exception as e: if isinstance(e, ImportError): if dcClass in e.message: self.validateClass(dcClass, importModule) generated = True finally: if self.wantOverwrite and not generated and dcClass not in self.ignoredClasses: self.validateClass(dcClass, importModule) def readDclasses(self): for i in xrange(self.dcfile.getNumClasses()): dclass = self.dcfile.getClass(i) if not dclass.isStruct(): classes = [dclass.getName(), dclass.getName() + 'AI'] self.classesTuples.append(classes) print self.classesTuples def readFields(self): for className in self.classesTuples: dcClass = self.dcfile.getClassByName(className[0]) if not dcClass: print 'Found import for %s but no dclass defined.' % className continue print 'Reading fields for dclass %s...' % className[0] self.className2Fields[className[0]] = [] for i in xrange(dcClass.getNumFields()): dcField = dcClass.getField(i) self.className2Fields[className[0]].append(dcField) if self.generateNonImportDclasses: self.generateField(dcField, className[0]) continue if self.dclass2module[className[0]].split('.')[0] not in ('direct', 'panda3d'): self.generateField(dcField, className[0]) def validateModule(self, importModule): if '/' in importModule: print 'FAILED FOR %s' % importModule return try: exec('import %s' % importModule) except: self.generateModule(importModule) def createModuleFolder(self, modulePath): try: os.makedirs('./' + modulePath) except WindowsError: folders = modulePath.split('/')[:-1] if not folders: return nextDir = folders[0] if not os.path.isdir(nextDir): os.makedirs(nextDir) os.chdir(nextDir) if len(folders) == 1: return self.createModuleFolder(folders[1]) os.chdir('..') def moveToModulePath(self, modulePath): try: os.chdir(modulePath) except WindowsError: directories = modulePath.split('/') if os.path.isdir(directories[0]): os.chdir(directories[0]) self.moveToModulePath(directories[1]) def generateModule(self, module): modulePath = module.replace('.', '/') if not os.path.isdir(modulePath): self.createModuleFolder(modulePath) self.moveToModulePath(modulePath) if '.' in module: for x in xrange(len(module.split('.'))): open('./__init__.py', 'w+') os.chdir('..') else: self.writeInitFile(module) def writeInitFile(self, module): open('./' + module + '/__init__.py', 'w+') def validateClass(self, dcClass, importModule): for classdel in CLASS_DELIMITERS: if self.ignoreTypes[classdel] is True: if classdel in dcClass: self.ignoredClasses.append(dcClass) if self.ignoreTypes[''] is True: if self.isClientFile(dcClass): self.ignoredClasses.append(dcClass) if dcClass in self.ignoredClasses: return print 'Generating class %s...' % dcClass self.generateClass(importModule, dcClass) def generateClass(self, importModule, className): if not importModule and self.generateNonImportDclasses: directory = '.' else: directory = importModule.replace('.', '/') if not os.path.isdir(directory): os.makedirs(directory) if className in self.dclass2subclass.keys(): f = open( directory + '/' + self.dclass2subclass[className] + '.py', 'w+' ) else: f = open( directory + '/' + className + '.py', 'w+' ) dcClassName = className for classdel in CLASS_DELIMITERS: if classdel in className: dcClassName = className.split(classdel)[0] dcClass = self.dcfile.getClassByName(dcClassName) parentClasses =[] file = "" if dcClass: for i in xrange(dcClass.getNumParents()): parentClass = dcClass.getParent(i).getName() for classdel in CLASS_DELIMITERS: if classdel in className: parentClass += classdel if parentClass not in self.dclass2module.keys() and parentClass not in IMPORTS: print 'Couldn\'t find defined import %s!' % parentClass baseClass = self.removeDelimiter(parentClass) if baseClass in IMPORTS: parentModule = IMPORTS.get(baseClass) print 'Using assumption parent module %s for parent class %s!' % (parentModule, parentClass) parentClasses.append(parentClass) else: continue else: parentClasses.append(parentClass) parentModule = IMPORTS.get(parentClass, self.dclass2module.get(parentClass)) file += 'from ' + parentModule + '.' + parentClass + ' import ' + parentClass + '\n' else: print 'Couldn\'t find dclass for %s.' % dcClassName file += '\n' file += 'class %s%s:\n' % (className, self.formatParentClasses(parentClasses)) file += '\n' if self.wantInit: file += INDENT + 'def __init__(self):\n' numFields = dcClass.getNumFields() for i in xrange(numFields): dcField = dcClass.getField(i) if dcField.hasDefaultValue(): defaultValue = self.getDefaultValueFromField(dcField) else: defaultValue = 'None' if dcField.getName().startswith('set'): file += INDENT + INDENT + 'self.' + dcField.getName()[3].lower() + dcField.getName()[4:] + ' = '\ + defaultValue file += '\n' file += INDENT + INDENT + 'return\n' f.write(file) f.close() def getDefaultValueFromField(self, f): valueString = f.formatData(f.getDefaultValue()) try: data = valueString.split(' = ')[1].replace(')', '') except: return 'None' if '[' in data: return '[]' elif '<' in data: return 'None' else: try: int(data) return '0' except: pass return 'None' def formatParentClasses(self, parentClasses): if len(parentClasses) == 1: if parentClasses: return '(%s)' % parentClasses[0] return '()' return str(tuple(parentClasses)).replace('\'', '') def removeDelimiter(self, className): return className[:-2] def generateField(self, dcField, className): if className not in self.dclass2module and self.generateNonImportDclasses: fileName = className else: fileName = self.dclass2module[className].replace('.', '/') + '/' + className if dcField.isAirecv(): for classdel in ('AI', 'UD'): if self.ignoreTypes[classdel] is True: continue if className + classdel in self.dclass2module.keys(): self.writeField(fileName, dcField, classDelimiter=classdel) elif self.generateNonImportDclasses and os.path.isfile(className + classdel + '.py'): self.writeField(fileName, dcField, classDelimiter=classdel) elif dcField.isBroadcast() and not dcField.isClsend(): for classdel in ('AI', 'UD'): if self.ignoreTypes[classdel] is True: continue if className + classdel in self.dclass2module.keys(): self.writeField(fileName, dcField, classDelimiter=classdel) elif self.generateNonImportDclasses and os.path.isfile(className + classdel + '.py'): self.writeField(fileName, dcField, classDelimiter=classdel) elif dcField.isClsend() and self.ignoreTypes[''] is False: self.writeField(fileName, dcField) else: for classdel in ('', 'AI', 'UD'): if self.ignoreTypes[classdel] is True: continue if className + classdel in self.dclass2module.keys(): self.writeField(fileName, dcField, classDelimiter=classdel) elif self.generateNonImportDclasses and os.path.isfile(className + classdel + '.py'): self.writeField(fileName, dcField, classDelimiter=classdel) def writeField(self, fileName, dcField, classDelimiter=''): f = open( fileName + classDelimiter + '.py', 'r+' ) lines = f.readlines() for line, i in zip(lines, xrange(len(lines))): if 'def %s' % dcField.getName() in line: if not self.wantOverwrite: f.close() return if lines[-1] != '\n': lines.append('\n') numargs = len(self.getParameterList(dcField)) if not numargs: f.close() return if self.getParameterList(dcField) == ['']: numargs = 0 print 'Writing field %s for %s...' % (dcField.getName(), dcField.getClass().getName() + classDelimiter) lines.append(INDENT + 'def %s%s:\n' % (dcField.getName(), self.getTodoString(numargs))) lines.append(INDENT + INDENT + '#' + str(dcField)) lines.append(INDENT + INDENT + 'return\n') f.seek(0, 0) f.writelines(lines) f.close() def getParameterList(self, dcField): try: return re.sub('\[.*]', '', str(dcField)).split(dcField.getName() + '(')[1].split(')')[0].split(',') except: return '' def getTodoString(self, n): if n == 0: return '(self)' if n == 1: return '(self, todo0)' s = ['self'] for i in xrange(n): s.append('todo%d' % i) return str(tuple(s)).replace('\'', '') def isClientFile(self, className): return 'AI' not in className and 'UD' not in className