1#!/usr/bin/env python2 2 3"""Generate.py 4 5> python Generate.py -h 6""" 7 8import os 9import sys 10from getopt import getopt 11import FixPath 12import MiddleKit 13 14if sys.platform == 'win32': 15 # without this, I can't see output from uncaught exceptions! 16 # perhaps this is caused by the recent incorporation of win32all (via DataTable)? 17 sys.stderr = sys.stdout 18 19 20class Generate(object): 21 22 def databases(self): 23 """Return a list with the names of the supported database engines.""" 24 return ['MSSQL', 'MySQL', 'PostgreSQL', 'SQLite'] 25 26 def main(self, args=sys.argv): 27 """Main method.""" 28 opt = self.options(args) 29 30 # Make or check the output directory 31 outdir = opt['outdir'] 32 if not os.path.exists(outdir): 33 os.mkdir(outdir) 34 elif not os.path.isdir(outdir): 35 print 'Error: Output target, %s, is not a directory.' % outdir 36 37 # Generate 38 if 'sql' in opt: 39 print 'Generating SQL...' 40 self.generate( 41 pyClass = opt['db'] + 'SQLGenerator', 42 model = opt['model'], 43 configFilename = opt.get('config'), 44 outdir = os.path.join(outdir, 'GeneratedSQL')) 45 if 'py' in opt: 46 print 'Generating Python...' 47 self.generate( 48 pyClass = opt['db'] + 'PythonGenerator', 49 model = opt['model'], 50 configFilename = opt.get('config'), 51 outdir=outdir) 52 model = MiddleKit.Core.Model.Model(opt['model'], 53 configFilename=opt.get('config'), havePythonClasses=0) 54 model.printWarnings() 55 56 def usage(self, errorMsg=None): 57 """Print usage information.""" 58 progName = os.path.basename(sys.argv[0]) 59 if errorMsg: 60 print '%s: error: %s' % (progName, errorMsg) 61 print 62 print '''\ 63Usage: %s --db DBNAME --model FILENAME \\ 64 [--sql] [--py] [--config FILENAME] [--outdir DIRNAME] 65 %s -h | --help 66 67 * Known databases include: %s. 68 * If neither --sql nor --py are specified, both are generated. 69 * If --outdir is not specified, 70 then the base filename (sans extension) is used. 71 * --config lets you specify a different config filename inside the model. 72 This is mostly useful for the regression test suite. 73''' % (progName, progName, ', '.join(self.databases())) 74 sys.exit(1) 75 76 def options(self, args): 77 """Get command line options.""" 78 # Command line dissection 79 if isinstance(args, basestring): 80 args = args.split() 81 optPairs, files = getopt(args[1:], 'h', 82 ['help', 'db=', 'model=', 'sql', 'py', 'config=', 'outdir=']) 83 if len(optPairs) < 1: 84 self.usage('Missing options.') 85 if len(files) > 0: 86 self.usage('Extra files or options passed.') 87 88 # Turn the cmd line optPairs into a dictionary 89 opt = {} 90 for key, value in optPairs: 91 if key.startswith('--'): 92 key = key[2:] 93 elif key.startswith('-'): 94 key = key[1:] 95 opt[key] = value 96 97 # Check for required opt, set defaults, etc. 98 if 'h' in opt or 'help' in opt: 99 self.usage() 100 if 'db' not in opt: 101 self.usage('No database specified.') 102 if 'model' not in opt: 103 self.usage('No model specified.') 104 if 'sql' not in opt and 'py' not in opt: 105 opt['sql'] = '' 106 opt['py'] = '' 107 if 'outdir' not in opt: 108 opt['outdir'] = os.curdir 109 110 return opt 111 112 def generate(self, pyClass, model, configFilename, outdir): 113 """Generate code using the given class, model and output directory. 114 115 The pyClass may be a string, in which case a module of the same name is 116 imported and the class extracted from that. The model may be a string, 117 in which case it is considered a filename of a model. 118 """ 119 if isinstance(pyClass, basestring): 120 module = __import__(pyClass, globals()) 121 pyClass = getattr(module, pyClass) 122 generator = pyClass() 123 if isinstance(model, basestring): 124 generator.readModelFileNamed(model, 125 configFilename=configFilename, havePythonClasses=False) 126 else: 127 generator.setModel(model) 128 generator.generate(outdir) 129 130 131if __name__ == '__main__': 132 Generate().main(sys.argv) 133