#----------------------------------------------------------------------------- # Name: PythonEditorModels.py # Purpose: # # Author: Riaan Booysen # # Created: 2002/02/09 # RCS-ID: $Id: PythonEditorModels.py,v 1.22 2005/07/06 12:55:36 riaan Exp $ # Copyright: (c) 2002 - 2005 # Licence: GPL #----------------------------------------------------------------------------- print 'importing Models.PythonEditorModels' import os, sys, pprint, imp, stat, types, tempfile from thread import start_new_thread from time import time, localtime, strftime from StringIO import StringIO import wx import Preferences, Utils import EditorHelper, ErrorStack from EditorModels import PersistentModel, SourceModel, EditorModel, BitmapFileModel import relpath, sourceconst True,False=1,0 (imgPyAppModel, imgModuleModel, imgPackageModel, imgSetupModel, imgPythonBinaryFileModel, ) = EditorHelper.imgIdxRange(5) class SourcePseudoFile(Utils.PseudoFileOutStore): def readlines(self): return self.output class ModuleModel(SourceModel): modelIdentifier = 'Module' defaultName = 'module' bitmap = 'Module.png' imgIdx = imgModuleModel ext = '.py' def __init__(self, data, name, editor, saved, app=None): self.app = app SourceModel.__init__(self, data, name, editor, saved) self.moduleName = os.path.split(self.filename)[1] self.lastRunParams = '' self.lastDebugParams = '' self.useInputStream = False if data: if Preferences.autoReindent: if not self.reindent(False): self.update() else: self.update() def destroy(self): SourceModel.destroy(self) del self.app def load(self, notify = True): SourceModel.load(self, False) if Preferences.autoReindent: if not self.reindent(): self.update() else: self.update() if notify: self.notify() def save(self, overwriteNewer=False): if Preferences.autoReindent: self.reindent(False) SourceModel.save(self, overwriteNewer) def saveAs(self, filename): oldFilename = self.filename SourceModel.saveAs(self, filename) if self.app: self.app.moduleSaveAsNotify(self, oldFilename, filename) self.moduleName = os.path.basename(filename) # update breakpoints from Debugger.Breakpoint import bplist bplist.renameFileBreakpoints(oldFilename, self.filename) if self.editor.debugger: self.editor.debugger.breakpts.refreshList() self.notify() _module = None def getModule(self): if self._module is None: wx.BeginBusyCursor() try: import moduleparse self._module = moduleparse.Module( self.moduleName, self.getDataAsLines()) finally: wx.EndBusyCursor() return self._module def initModule(self): # Don't parse the module until it's needed. self._module = None def refreshFromModule(self): """ Must call this method to apply changes made to the module object. """ self.setDataFromLines(self.getModule().getEOLFixedLines()) self.notify() def renameClass(self, oldname, newname): pass def update(self): self.initModule() EditorModel.update(self) def runInThread(self, filename, args, interpreterPath, inpLines=[], execStart=None, execFinish=None): cwd = os.path.abspath(os.getcwd()) newCwd = os.path.dirname(os.path.abspath(filename)) os.chdir(newCwd) try: cmd = '"%s" %s %s'%(interpreterPath, os.path.basename(filename), args) from ModRunner import PopenModuleRunner#, ExecFinishEvent runner = PopenModuleRunner(None, newCwd) runner.run(cmd, inpLines, execStart) #wx.PostEvent(self.editor, ExecFinishEvent(runner)) if execFinish: wx.CallAfter(execFinish, runner) finally: if os: os.chdir(cwd) def run1(self, args = '', execStart=None, execFinish=None): """ Excecute the current saved image of the application. """ if self.savedAs: filename = self.assertLocalFile() self.editor.statusBar.setHint('Running %s...' % filename) if Preferences.minimizeOnRun: self.editor.minimizeBoa() inpLines = [] if self.useInputStream and self.editor.erroutFrm.inputPage: inpLines = StringIO( self.editor.erroutFrm.inputPage.GetValue()).readlines() start_new_thread(self.runInThread, (filename, args, Preferences.getPythonInterpreterPath(), inpLines, execStart, execFinish)) #self.runInThread(filename, args, # Preferences.getPythonInterpreterPath(), inpLines, # execStart, execFinish) def run(self, args = '', execStart=None, execFinish=None): """ Excecute the current saved image of the application. """ if self.savedAs: filename = self.assertLocalFile() self.editor.statusBar.setHint('Running %s...' % filename) if Preferences.minimizeOnRun: self.editor.minimizeBoa() inpLines = [] if self.useInputStream and self.editor.erroutFrm.inputPage: inpLines = StringIO( self.editor.erroutFrm.inputPage.GetValue()).readlines() cwd = os.path.abspath(os.getcwd()) newCwd = os.path.dirname(os.path.abspath(filename)) interp = Preferences.getPythonInterpreterPath() basename = os.path.basename(filename) os.chdir(newCwd) try: cmd = '"%s" %s %s'%(interp, basename, args) from ModRunner import wxPopenModuleRunner runner = wxPopenModuleRunner(self.editor.erroutFrm, newCwd) runner.run(cmd, inpLines, execFinish) execStart(runner.pid, os.path.basename(interp), basename) finally: if os: os.chdir(cwd) # XXX Not used! def runAsScript(self): filename = self.assertLocalFile() execfile(filename) def compile(self): import ModRunner oldErr = sys.stderr sys.stderr = ErrorStack.RecFile() try: cmr = ModRunner.CompileModuleRunner(self.editor.erroutFrm) cmr.run(self.filename, self.data+'\n\n', self.modified) serr = ErrorStack.errorList(sys.stderr) cmr.checkError(serr, 'Compiled') finally: sys.stderr = oldErr return len(serr) def cyclops(self, args='', execStart=None, execFinish=None): """ Run the saved application thru Cyclops """ if self.savedAs: cwd = os.path.abspath(os.getcwd()) filename = self.assertLocalFile() os.chdir(os.path.dirname(filename)) page = '' try: name = os.path.basename(filename) report = tempfile.mktemp() # execute Cyclops in Python with module as parameter command = '"%s" "%s" "%s" "%s"'%( Preferences.getPythonInterpreterPath(), Utils.toPyPath('RunCyclops.py'), name, report) wx.Execute(command, True) # read report that Cyclops generated page = open(report, 'r').read() os.remove(report) finally: os.chdir(cwd) return page else: wx.LogWarning('Save before running Cyclops') raise 'Not saved yet!' def debug(self, params=None, cont_if_running=0, cont_always=0, temp_breakpoint=None): if self.savedAs: debugger = self.editor.debugger if not debugger: from Debugger import Debugger filename = self.assertLocalFile(self.filename) debugger = Debugger.DebuggerFrame(self.editor, filename) debugger.setDebugClient() if params is not None: # pass [] to clear parameters debugger.setParams(params) self.editor.debugger = debugger debugger.Show() debugger.initSashes() debugger.ensureRunning(cont_if_running, cont_always, temp_breakpoint) def profile(self): filename = self.assertLocalFile() #statFile = os.path.splitext(filename)[0]+'.prof' statFile = tempfile.mktemp() if os.path.exists(statFile): modtime = os.stat(statFile)[stat.ST_MTIME] else: modtime = None profDir = os.path.dirname(filename) cwd = os.path.abspath(os.getcwd()) os.chdir(profDir) try: profCmd = """"%s" -c "import profile;profile.run('execfile('+chr(34)+%s+chr(34)+')', '%s')" """.strip() cmd = profCmd % (`Preferences.getPythonInterpreterPath()`[1:-1], `os.path.basename(filename)`, `statFile`[1:-1]) if hasattr(self, 'app'): app = self.app else: app = None from ModRunner import ExecuteModuleRunner runner = ExecuteModuleRunner(None, profDir) self.editor.statusBar.setHint('Profiling %s...'%filename) runner.run(cmd) self.editor.statusBar.setHint('Finished profiling.') finally: os.chdir(cwd) return statFile, modtime, profDir def addModuleInfo(self, prefs): # XXX Check that module doesn't already have an info block dollar = '$' # has to be obscured from CVS :) prefs['Name'] = self.moduleName prefs['Created'] = strftime('%Y/%m/%d', localtime(time())) prefs['RCS-ID'] = '%sId: %s %s' % (dollar, self.moduleName , dollar) self.data = (sourceconst.defInfoBlock % prefs) + self.data self.modified = True self.update() self.notify() def reindent(self, updateModulePage=True): from ExternalLib import reindent self.refreshFromViews() eol = Utils.getEOLMode(self.data) file = SourcePseudoFile(self.getDataAsLines()) ri = reindent.Reindenter(file, eol=eol) try: if ri.run(): file.output = [] ri.write(file) newData = ''.join(file.output) modified = self.data != newData self.modified = self.modified or modified if modified: self.data = newData if updateModulePage: self.editor.updateModulePage(self) self.update() self.notify() self.editor.statusBar.setHint(\ 'Code reformatted (indents and or EOL characters fixed)') return True except Exception, error: self.editor.statusBar.setHint(\ 'Reindent failed - %s : %s' % (error.__class__, str(error)) , 'Error') return False def getSimpleRunnerSrc(self): return sourceconst.simpleModuleRunSrc def disassembleSource(self): import dis try: code = compile(self.data, self.filename, 'exec') except: oldOut = sys.stdout sys.stdout = Utils.PseudoFileOutStore() try: print "''' Code does not compile\n\n Disassembly of Traceback:\n'''" try: dis.distb(sys.exc_info()[2]) except: print "''' Could not disassemble traceback '''\n" return sys.stdout.read() finally: sys.stdout = oldOut oldOut = sys.stdout sys.stdout = Utils.PseudoFileOutStore() try: try: dis.disco(code) except: raise return sys.stdout.read() finally: sys.stdout = oldOut return 'Invisible code' def runLint(self): filename = self.assertLocalFile() from ExternalLib import pylint import StringIO pylint.pylint(StringIO.StringIO(self.data), filename) if pylint.warnings: return ErrorStack.buildLintWarningList(pylint.warnings[:]) def buildImportSearchPath(self): try: filename = self.assertLocalFile() except AssertionError: srchpath = [] else: srchpath = [os.path.dirname(filename)] if self.app: try: appfilename = self.app.assertLocalFile() except AssertionError: pass else: srchpath.insert(0, os.path.dirname(appfilename)) return srchpath def findModule(self, modName, impName=''): """ Tries it's best to locate given module name or raise ImportError """ # first search std python modules stdPyPath = sys.path[1:] srchpath = stdPyPath[:] for name in modName.split('.'): try: file, path, (ext, mode, tpe) = imp.find_module(name, srchpath) except ImportError: if srchpath == stdPyPath: # else search module and app dirs srchpath = self.buildImportSearchPath() file, path, (ext, mode, tpe) = imp.find_module(name, srchpath) else: raise if srchpath == stdPyPath: srchpath = [] if tpe == imp.PKG_DIRECTORY: srchpath.append(path) continue elif tpe == imp.PY_SOURCE: # handle from [package.]module import name return path, 'name' if tpe == imp.PY_COMPILED: self.editor.setStatus('Compiled file found, check sys.path!', 'Warning', True) raise ImportError('Compiled file found') else: raise ImportError('Unhandled import type') # handle from package import module if srchpath and srchpath != stdPyPath: if impName: path = os.path.join(srchpath[-1], impName+'.py') if os.path.isfile(path): return path, 'module' else: return srchpath[-1], 'package' #print '%s not found in %s'%(modName, `srchpath`) raise ImportError('Module not found') def importInShell(self): modDir, modFile = os.path.split(self.assertLocalFile()) modName = os.path.splitext(modFile)[0] if self.app: execDir = os.path.dirname(self.app.assertLocalFile()) if execDir != modDir: p, m = os.path.split(relpath.relpath(execDir, self.assertLocalFile())) p = p.replace('/', '.') p = p.replace('\\', '.') pckName = p impExecStr = 'from %s import %s'%(pckName, modName) else: impExecStr = 'import %s'%modName else: execDir = modDir impExecStr = 'import %s'%modName shell = self.editor.shell if execDir not in sys.path: sys.path.append(execDir) shell.pushLine("print '## Appended to sys.path'") else: info = '' shell.pushLine(impExecStr, impExecStr) if shell.lastResult != 'stderr': return 'Import of %s successfull'%modName, 'Info' else: return 'Import of %s failed'%modName, 'Error' def reloadInShell(self): modDir, modFile = os.path.split(self.assertLocalFile()) modName = os.path.splitext(modFile)[0] impExecStr = 'reload(%s)'%modName shell = self.editor.shell shell.pushLine(impExecStr, impExecStr) if shell.lastResult != 'stderr': return 'Reload of %s successfull'%modName, 'Info' else: return 'Reload of %s failed'%modName, 'Error' def findGlobalDict(self, name): s = name+' =' pos = self.data.find(s) if pos == -1: raise 'Global dict %s not found in the module, please add '\ '"%s = {}" as a global variable.'%(name, name) end = self.data.find('}\n', pos + len(s) +1) + 1 if not end: end = self.data.find('}\r\n', pos + len(s) +1) + 1 if not end: raise 'Global dict %s not terminated properly, please fix it.'%name return pos + len(s), end def readGlobalDict(self, name): start, end = self.findGlobalDict(name) try: return eval(Utils.toUnixEOLMode(self.data[start:end]), {'wx': wx}) except Exception, err: raise '"%s" must be a valid dictionary global dict.\nError: %s'%(name, str(err)) def writeGlobalDict(self, name, dct): start, end = self.findGlobalDict(name) eol = Utils.getEOLMode(self.data) self.data = self.data[:start]+pprint.pformat(dct).replace('\n', eol)+\ self.data[end:] def buildResourceSearchList(self): searchPath = [os.path.abspath(os.path.dirname(self.localFilename()))] if self.app: searchPath.append(os.path.abspath(os.path.dirname(self.app.localFilename()))) return searchPath def loadResource(self, importName, searchPath): d={} syspath = sys.path[:] sys.path[:] = searchPath try: try: exec 'import %s'%importName in d exec 'reload(%s)'%importName in d finally: sys.path[:] = syspath imageMod = eval(importName, d) del d['__builtins__'] rootModName, rootMod = d.items()[0] finally: #try: del sys.modules[importName] #except KeyError: pass del d return imageMod, rootModName, rootMod def assureResourceLoaded(self, importName, resources, searchPath=None, specialAttrs=None, report=False): if searchPath is None: searchPath = self.buildResourceSearchList() try: f, fn, desc = Utils.find_dotted_module(importName, searchPath) except ImportError: if report: self.editor.setStatus('Could not find %s'%importName, 'Error') return False if f is None: return False f.close() import Controllers Model, main = Controllers.identifyFile(fn) for ResourceClass in Controllers.resourceClasses: if issubclass(Model, ResourceClass): try: imageMod, rootName, rootMod = self.loadResource(importName, searchPath) resources[importName] = imageMod specialAttrs[rootName] = rootMod if report: self.editor.setStatus('Loaded resource: %s'%importName) except ImportError: self.editor.setStatus('Could not load %s'%importName, 'Error') return False return True if report: self.editor.setStatus('%s is not a valid Resource Module'%importName, 'Error') return False def readResources(self, mod, cls, specialAttrs): resources = {} searchPath = self.buildResourceSearchList() for impName in mod.imports.keys(): self.assureResourceLoaded(impName, resources, searchPath, specialAttrs) return resources class ClassModel(ModuleModel): """ Represents access to 1 maintained main class in the module. This class is identified by the 3rd header entry #Boa:Model:Class """ def __init__(self, data, name, main, editor, saved, app = None): self.main = main self.mainConstr = None ModuleModel.__init__(self, data, name, editor, saved, app) def renameMain(self, oldName, newName): self.getModule().renameClass(oldName, newName) self.main = newName idx = 0 for line in self.getModule().source: if line: if line[0] != '#': break header = line.strip().split(':') if (len(header) == 3) and (header[0] == sourceconst.boaIdent): self.getModule().source[idx] = \ ':'.join((header[0], header[1], newName)) break else: break idx = idx + 1 class ImportRelationshipMix: def buildImportRelationshipDict(self, modules): relationships = {} tot = len(modules) self.editor.statusBar.progress.SetRange(tot) try: prog = 0 totLOC = 0 classCnt = 0 # XXX Rewrite in terms of transport for module in modules: self.editor.statusBar.progress.SetValue(prog) prog = prog + 1 self.editor.setStatus('Parsing '+module+'...') #module = self.modules[moduleName] #filename = self.normaliseModuleRelativeToApp(module[2]) if module[:7] != 'file://': print '%s skipped, only local files supported for Imports View' else: module = module[7:] try: f = open(module) except IOError: print "couldn't load %s" % module continue else: data = f.read() f.close() name = os.path.splitext(os.path.basename(module))[0] model = ModuleModel(data, name, self.editor, 1) relationships[name] = model.getModule() #.imports totLOC = totLOC + model.getModule().loc classCnt = classCnt + len(model.getModule().classes) print 'Project LOC: %d,\n%d classes in %d modules.'%(totLOC, classCnt, len(modules)) finally: self.editor.statusBar.progress.SetValue(0) self.editor.statusBar.setHint('') return relationships class PackageModel(ModuleModel, ImportRelationshipMix): """ Must be constructed in a valid path, name being filename, actual name will be derived from path """ modelIdentifier = 'Package' defaultName = 'package' bitmap = 'Package.png' imgIdx = imgPackageModel pckgIdnt = '__init__.py' ext = '.py' def __init__(self, data, name, editor, saved, app=None): ModuleModel.__init__(self, data, name, editor, saved, app) self.packagePath = os.path.split(self.filename)[0] self.packageName = os.path.split(self.packagePath)[1] self.savedAs = True self.modified = False def openPackage(self, name): if self.views.has_key('Folder'): notebook = self.views['Folder'] else: notebook = None self.editor.openOrGotoModule(os.path.join(self.packagePath, name, self.pckgIdnt), notebook=notebook) def openFile(self, name): if self.views.has_key('Folder'): notebook = self.views['Folder'] else: notebook = None self.editor.openOrGotoModule(os.path.join(self.packagePath, name + self.ext), notebook=notebook) def generateFileList(self): """ Generate a list of modules and packages in the package path """ from Explorers.Explorer import openEx transp = openEx(self.packagePath) filtered = [] for item in transp.openList(): if item.treename != '__init__.py' and \ (os.path.splitext(item.treename)[1] == self.ext or \ item.imgIdx == imgPackageModel): filtered.append(item) return filtered def getPageName(self): return self.packageName def buildImportRelationshipDict(self): mods = [] for module in self.generateFileList(): mods.append('file://'+module.resourcepath) return ImportRelationshipMix.buildImportRelationshipDict(self, mods) class PythonBinaryFileModel(PersistentModel): modelIdentifier = 'PythonBinary' defaultName = '' bitmap = 'PythonBinary.png' imgIdx = imgPythonBinaryFileModel ext = '.pybin' SimpleTypes = [types.StringType, types.IntType, types.FloatType, types.NoneType, types.DictionaryType, types.ListType, types.TupleType] try: SimpleTypes.append(types.UnicodeType) except AttributeError: pass FunctionTypes = [types.FunctionType, types.BuiltinFunctionType] MethodTypes = [types.MethodType, types.BuiltinMethodType] PrivMethodTypeNames = ['method_descriptor', 'method-wrapper'] class PyExtTypeData: def __init__(self, Type): self.methods = [] self.attrs = {} for name in dir(Type): attr = getattr(Type, name) AttrType = type(attr) if AttrType in MethodTypes or \ AttrType.__name__ in PrivMethodTypeNames: self.methods.append(name) else: self.attrs[name] = attr class PyExtModuleData: def __init__(self, module): self.classes = {} self.functions = {} self.attrs = {} self.modules = {} for name in dir(module): attr = getattr(module, name) AttrType = type(attr) if AttrType in SimpleTypes: self.attrs[name] = attr elif AttrType is types.ClassType: self.classes[name] = PyExtTypeData(attr) elif AttrType in FunctionTypes: self.functions[name] = attr elif AttrType is types.ModuleType: self.modules[name] = PyExtModuleData(attr) elif hasattr(attr, '__class__'): self.classes[name] = PyExtTypeData(attr) else: # fallback attributes self.attrs[name] = attr class PythonExtensionFileModel(PythonBinaryFileModel): modelIdentifier = 'PythonExtension' defaultName = '' bitmap = 'PythonBinary.png' imgIdx = imgPythonBinaryFileModel ext = '.pyd' def __init__(self, data, name, editor, saved): # XXX data not read as binary anyway PythonBinaryFileModel.__init__(self, '', name, editor, True) filename = self.checkLocalFile() dirName, pydName = os.path.split(filename) modName = os.path.splitext(pydName)[0] sys.path.insert(0, dirName) try: self.module = __import__(modName) finally: del sys.path[0] self.moduleData = PyExtModuleData(self.module) class PythonCompiledFileModel(PythonBinaryFileModel): modelIdentifier = 'PythonCompiled' defaultName = '' bitmap = 'PythonBinary.png' imgIdx = imgPythonBinaryFileModel ext = '.pyc' class BaseAppModel(ClassModel, ImportRelationshipMix): def __init__(self, data, name, main, editor, saved, openModules): self.moduleModels = {} self.textInfos = {} self.unsavedTextInfos = [] self.modules = {} self.app = self ClassModel.__init__(self, data, name, main, editor, saved, self) if data: self.update() self.notify() # Connect all open modules to this app obj if they are defined in # the app's modules import Controllers abspaths = self.absModulesPaths() for modPage in openModules.values(): if modPage.model.modelIdentifier not in Controllers.appModelIdReg \ and hasattr(modPage.model, 'app') and \ modPage.model.filename in abspaths: modPage.model.app = self def absModulesPaths(self): modules = self.modules.keys() abspaths = [] for moduleName in modules: abspaths.append(self.normaliseModuleRelativeToApp(self.modules[moduleName][2])) return abspaths def convertToUnixPath(self, filename): # Don't convert absolute windows paths, will stay illegal until saved if os.path.splitdrive(filename)[0] != '': return filename else: return filename.replace('\\', '/') def save(self, overwriteNewer=False): ClassModel.save(self, overwriteNewer) for tin in self.unsavedTextInfos: fn = os.path.join(os.path.dirname(self.filename), tin) data = self.textInfos[tin] if data: from Explorers.Explorer import openEx, TransportError try: f = openEx(fn) f.save(f.currentFilename(), data) except TransportError, err: pass self.unsavedTextInfos = [] def saveAs(self, filename): for mod in self.modules.keys(): self.modules[mod][2] = self.convertToUnixPath(\ relpath.relpath(os.path.dirname(filename), self.normaliseModuleRelativeToApp(self.modules[mod][2]))) self.writeModules() ClassModel.saveAs(self, filename) self.notify() def findImports(self): impPos = self.data.find(sourceconst.defImport.strip()) impPos = self.data.find('import', impPos + 1) # XXX Add if not found if impPos == -1: raise 'Module import list not found in application' impEnd = self.data.find('\012', impPos + len('import') +1) + 1 if impEnd == -1: raise 'Module import list not terminated' return impPos + len('import'), impEnd def idModel(self, name, src=None): # XXX This should be cached until rename or delete absPath = self.normaliseModuleRelativeToApp(self.modules[name][2]) import Controllers from Explorers.Explorer import splitURI prot, cat, res, fn = splitURI(absPath) if src is None: if self.editor.modules.has_key(name): self.moduleModels[name], main = identifySource( self.editor.modules[name].model.getDataAsLines()) if self.editor.modules.has_key(absPath): self.moduleModels[name], main = identifySource( self.editor.modules[absPath].model.getDataAsLines()) else: try: self.moduleModels[name], main = \ Controllers.identifyFile(res, localfs=prot=='file') except: pass else: self.moduleModels[name], main = identifySource(src) def readModules(self): self.modules = self.readGlobalDict('modules') for mod in self.modules.keys(): self.idModel(mod) def writeModules(self, notify=True): self.writeGlobalDict('modules', self.modules) self.modified = True self.editor.updateTitle() self.editor.updateModulePage(self) if notify: self.notify() def viewAddModule(self): fn = self.editor.openFileDlg() if fn: self.addModule(fn, '') def addModule(self, filename, descr, source=None): name, ext = os.path.splitext(os.path.basename(filename)) if self.modules.has_key(name): raise Exception('Module name exists in application') if self.savedAs: relative = relpath.relpath(os.path.dirname(self.filename), filename) else: relative = filename self.modules[name] = [0, descr, self.convertToUnixPath(relative)] self.idModel(name, source) self.writeModules() def removeModule(self, name): if not self.modules.has_key(name): raise 'No such module in application' del self.modules[name] self.writeModules() def editModule(self, oldname, newname, main, descr): relpath = self.modules[oldname][2] if oldname != newname: del self.modules[oldname] self.modules[newname] = [main, descr, relpath] self.writeModules() def splitProtFile(self, uri): protsplit = uri.split('://') if len(protsplit) == 1: return 'file', uri elif len(protsplit) == 2: return protsplit else: raise 'Unhandled protocol %s'%uri def moduleFilename(self, name): """ Return absolute filename of the given module """ if not self.modules.has_key(name): raise 'No such module in application: '+name prot, modFilename = self.splitProtFile(self.modules[name][2]) if self.savedAs: if os.path.isabs(modFilename) or prot != 'file': absPath = self.modules[name][2] else: appProt, appFilename = self.splitProtFile(self.filename) absPath = appProt+'://'+self.convertToUnixPath(os.path.normpath( os.path.join(os.path.dirname(appFilename), modFilename))) else: #absPath = name + ModuleModel.ext absPath = self.modules[name][2] return absPath def updateAutoCreateImports(self, oldName, newName): """ Rename module in import list. Only autocreated modules should be on this list. The module is modified and the model is not updated""" module = self.getModule() if module.imports.has_key(oldName): impLine = module.imports[oldName][0]-1 # read in the import line line = module.source[impLine] impIndent = line.find('import') imports = line[7+impIndent:].strip().split(', ') impIdx = imports.index(oldName) imports[impIdx] = newName module.imports[newName] = module.imports[oldName] del module.imports[oldName] module.source[impLine] = 'import '+', '.join(imports) return impIdx return None def updateMainFrameModuleRefs(self, oldName, newName): """ Replace references to old main module with new main module """ module = self.getModule() block = module.classes[sourceconst.boaClass].methods['OnInit'] mainDef = 'self.main = %s.' fndOldStr = mainDef % oldName repNewStr = mainDef % newName for idx in range(block.start, block.end): line = module.source[idx] newLine = line.replace(fndOldStr, repNewStr) if newLine != line: module.source[idx] = newLine def changeMainFrameModule(self, newMainFrameModule): """ Select a new main frame module """ if len(self.viewsModified): self.refreshFromViews() # determine which module is the main module module = self.getModule() #for mod, props in filter(lambda v: v[1][0], self.modules.items()): for mod, props in [i for i in self.modules.items() if i[1][0]]: impLine = module.imports[mod][0]-1 line = module.source[impLine] impIndent = line.find('import') imports = line[7+impIndent:].split(', ') if len(imports) and imports[0] == mod: try: impIdx = imports.index(newMainFrameModule) except ValueError: impIdx = 0 del imports[impIdx] imports.insert(0, newMainFrameModule) module.source[impLine] = impIndent*' '+'import '+', '.join(imports) self.updateMainFrameModuleRefs(mod, newMainFrameModule) self.refreshFromModule() # Update autocreation status props[0] = 0 self.modules[newMainFrameModule][0] = 1 self.writeModules(False) self.update() self.notify() break else: raise 'No main frame module found in application' def moduleSaveAsNotify(self, module, oldFilename, newFilename): if module != self: newName, ext = os.path.splitext(os.path.basename(newFilename)) oldName = os.path.splitext(os.path.basename(oldFilename))[0] if not self.modules.has_key(oldName): raise 'Module does not exists in application' if self.savedAs: relative = relpath.relpath(os.path.dirname(self.filename), newFilename) else: relative = newFilename if newName != oldName: self.modules[newName] = self.modules[oldName] del self.modules[oldName] self.modules[newName][2] = self.convertToUnixPath(relative) # Check if it's autocreated module if self.modules[newName][0]: if len(self.viewsModified): self.refreshFromViews() impIdx = self.updateAutoCreateImports(oldName, newName) if impIdx is not None: # check if it's the main module, first in the import list is # always the main module if not impIdx: self.updateMainFrameModuleRefs(oldName, newName) # preserve modified modules mods = self.modules self.refreshFromModule() self.modules = mods self.writeModules() self.update() def crashLog(self): err = ErrorStack.crashError(os.path.splitext(self.assertLocalFile())[0]+'.trace') if err: frm = self.editor.erroutFrm if frm: frm.updateCtrls(err) frm.display(err) return frm else: wx.LogError('Trace file not found. Run with command line param -T') return None def openModule(self, name): from Explorers.Explorer import TransportError try: return self.editor.openOrGotoModule(self.moduleFilename(name), self) except TransportError, err: if str(err) == 'Unhandled transport' and err[1][0] == 'none': if wx.MessageBox('Unsaved file no longer open in the Editor.\n' 'Remove it from application modules ?', 'Missing file', wx.YES_NO | wx.ICON_QUESTION) == wx.YES: self.removeModule(name) return None, None else: raise def normaliseModuleRelativeToApp(self, relFilename): """ Normalise relative paths to absolute paths """ if not self.savedAs or relFilename.startswith('none://'): return relFilename else: protsplit = self.filename.split('://') if len(protsplit) == 1: prot, appFilename = 'file', self.filename elif len(protsplit) == 2: prot, appFilename = protsplit elif len(protsplit) == 3: prot, archive, appFilename = protsplit else: raise Exception, 'Unhandled protocol during normalisation:%s'%protsplit if prot == 'zip': return relFilename normedpath = os.path.normpath(os.path.join(os.path.dirname(appFilename), relFilename)) if prot == 'file': return '%s://%s' %(prot, normedpath) else: return '%s://%s' %(prot, normedpath.replace('\\', '/')) def buildImportRelationshipDict(self): return ImportRelationshipMix.buildImportRelationshipDict(self, self.absModulesPaths()) def update(self): self.readModules() ClassModel.update(self) def loadTextInfo(self, viewName): from Explorers.Explorer import openEx, TransportError fn = os.path.join(os.path.dirname(self.filename), viewName) ti = openEx(fn) try: data = ti.load() except TransportError, err: data = '' self.textInfos[viewName] = data class PyAppModel(BaseAppModel): modelIdentifier = 'PyApp' defaultName = 'PyApp' bitmap = 'PythonApplication.png' imgIdx = imgPyAppModel def getDefaultData(self): return (sourceconst.defEnvPython + sourceconst.defSig + \ sourceconst.defPyApp) %{'modelIdent': self.modelIdentifier, 'main': 'main'} class SetupModuleModel(ModuleModel): modelIdentifier = 'setup' defaultName = 'Setup' bitmap = 'Setup.png' imgIdx = imgSetupModel def __init__(self, data, name, editor, saved, app=None): ModuleModel.__init__(self, data, name, editor, saved, app) if data: self.update() self.notify() def getDefaultData(self): return (sourceconst.defSetup_py) % {'name': 'default', 'version': '0.1', 'scripts': ''} def getPageName(self): return 'setup (%s)' % os.path.basename(os.path.dirname(self.filename)) ## def saveAs(self, filename): ## # catch image type changes ## newExt = os.path.splitext(filename)[1].lower() ## oldExt = os.path.splitext(self.filename)[1].lower() ## updateViews = 0 ## if newExt != oldExt: ## updateViews = 1 ## bmp = wx.BitmapFromImage(wx.ImageFromStream(StringIO(self.data))) ## fn = tempfile.mktemp(newExt) ## try: ## bmp.SaveFile(fn, self.extTypeMap[newExt]) ## except KeyError: ## raise Exception, '%s image file types not supported'%newExt ## try: ## # convert data to new image format ## self.data = open(fn, 'rb').read() ## finally: ## os.remove(fn) ## ## # Actually save the file ## PersistentModel.saveAs(self, filename) #------------------------------------------------------------------------------- def identifyHeader(headerStr): header = headerStr.split(':') if len(header) and (header[0] == sourceconst.boaIdent) and \ EditorHelper.modelReg.has_key(header[1]): return EditorHelper.modelReg[header[1]], header[2] return ModuleModel, '' def identifySource(source): """ Return appropriate model for given Python source. The logic is a copy paste from above func """ for line in source: if line: if line[0] != '#': return ModuleModel, '' headerInfo = identifyHeader(line.strip()) if headerInfo[0] != ModuleModel: return headerInfo else: return ModuleModel, '' return ModuleModel, '' #------------------------------------------------------------------------------- EditorHelper.modelReg[PythonBinaryFileModel.modelIdentifier] = PythonBinaryFileModel EditorHelper.inspectableFilesReg['.py'] = ModuleModel