From 549baefa5409254be3ea265b4069f9d8c8d7368e Mon Sep 17 00:00:00 2001 From: Capocchi L Date: Fri, 6 Nov 2020 18:17:10 +0100 Subject: [PATCH 1/3] try to improve the rename function and Zip class --- Container.py | 91 +++++++++++++++++--------------- Domain/M1.amd | Bin 0 -> 2724 bytes LibraryTree.py | 29 +++++++---- Mixins/Savable.py | 128 ++++++++++++++++++++++++---------------------- ZipManager.py | 67 +++++++++++++++++++++++- 5 files changed, 202 insertions(+), 113 deletions(-) create mode 100644 Domain/M1.amd diff --git a/Container.py b/Container.py index 908d0eda..ff42f728 100644 --- a/Container.py +++ b/Container.py @@ -3651,55 +3651,66 @@ def __setstate__(self, state): else: sys.stderr.write(_("Error in setstate for CodeBlock: %s\n"%str(cls))) - state['bad_filename_path_flag'] = False - ### if the model path is empty and the python path is wrong - if model_path == '' and not os.path.exists(python_path) :#and zipfile.is_zipfile(os.path.dirname(python_path))): + ### if the python path is wrong + if not os.path.exists(python_path) :#and zipfile.is_zipfile(os.path.dirname(python_path))): + ### if the model path is empty (for pure python file - not .amd or .cmd) + if model_path == '' : + path = python_path - path = python_path - - ### if DOMAIN is in python_path - if dir_name in python_path: - - ### try to find in DOMAIN directory - path = os.path.join(os.path.dirname(DOMAIN_PATH), relpath(str(python_path[python_path.index(dir_name):]).strip('[]'))) + ### if DOMAIN is in python_path + if dir_name in python_path: + + ### try to find in DOMAIN directory + path = os.path.join(os.path.dirname(DOMAIN_PATH), relpath(str(python_path[python_path.index(dir_name):]).strip('[]'))) - ### try to find it in exportedPathList (after Domain check) and recent opened file + ### try to find it in exportedPathList (after Domain check) and recent opened file + if not os.path.exists(path): + mainW = wx.GetApp().GetTopWindow() + if hasattr(mainW,'exportPathsList') and hasattr(mainW,'openFileList'): + for p in mainW.exportPathsList+mainW.openFileList: + lib_name = os.path.basename(p) + if lib_name !='' and lib_name in path: + path = p+path.split(lib_name)[-1] + break + else: + ### try to find if python_path contains a directory wich is also in Domain + ### subdirectories of Domain + subdirectories = os.listdir(DOMAIN_PATH) + ### for all directories if the directory is in python_path (excluding the file .py (-1)) + for dir in subdirectories: + if dir in python_path.split(os.sep)[0:-1]: + ### yes, the python_path is wrong but we find that in the Domain there is a directory with the same name + a = python_path.split(dir+os.sep) + path = os.path.join(DOMAIN_PATH,dir,a[-1]) + break + + ### try to find the python_path in recent opened file directory if not os.path.exists(path): mainW = wx.GetApp().GetTopWindow() if hasattr(mainW,'exportPathsList') and hasattr(mainW,'openFileList'): - for p in mainW.exportPathsList+mainW.openFileList: - lib_name = os.path.basename(p) - if lib_name !='' and lib_name in path: - path = p+path.split(lib_name)[-1] + for a in [os.path.dirname(p) for p in mainW.exportPathsList+mainW.openFileList]: + p = os.path.join(a,os.path.basename(python_path)) + if os.path.exists(p): + path = p break - else: - ### try to find if python_path contains a directory wich is also in Domain - ### subdirectories of Domain - subdirectories = os.listdir(DOMAIN_PATH) - ### for all directories if the directory is in python_path (excluding the file .py (-1)) - for dir in subdirectories: - if dir in python_path.split(os.sep)[0:-1]: - ### yes, the python_path is wrong but we find that in the Domain there is a directory with the same name - a = python_path.split(dir+os.sep) - path = os.path.join(DOMAIN_PATH,dir,a[-1]) - break - - ### try to find the python_path in recent opened file directory - if not os.path.exists(path): - mainW = wx.GetApp().GetTopWindow() - if hasattr(mainW,'exportPathsList') and hasattr(mainW,'openFileList'): - for a in [os.path.dirname(p) for p in mainW.exportPathsList+mainW.openFileList]: - p = os.path.join(a,os.path.basename(python_path)) - if os.path.exists(p): - path = p - break - ### if path is always wrong, flag is visible - if os.path.exists(path) : - state['python_path'] = path + ### if path is always wrong, flag is visible + if os.path.exists(path) : + state['python_path'] = path + else: + state['bad_filename_path_flag'] = True + + ### for .cmd or .amd else: - state['bad_filename_path_flag'] = True + pass + #with zipfile.ZipFile(model_path) as zf: + # ### find all python files + # for file in zf.namelist(): + # r = repr(zf.read(file)) + # if file.endswith(".py") and ('DomainBehavior' in r or 'DomainStructure' in r): + # #state['python_path'] = os.path.join(model_path, os.path.basename(model_path).replace('.amd','.py').replace('.cmd','.py')) + # state['bad_filename_path_flag'] = file != state['python_path'] ### if the fileName attribut dont exist, we define it into the current devsimpy directory (then the user can change it from Property panel) if 'args' in state: diff --git a/Domain/M1.amd b/Domain/M1.amd new file mode 100644 index 0000000000000000000000000000000000000000..ba8260b97d691773ec94c486b4510eb296060fb4 GIT binary patch literal 2724 zcmcgu-HzKt6yDMnDRYO^OD;xTiA}2}@=xqdRu$4V+eJ|~+t4kMkdUm2J$9yb?2%_2 z8m{0Hi3i}83toX&;ElNEJ9e@(OF=@daN_aIIdlHLbLQByXSeU%JD}@!nSS}R|KqQ} z-#Iv-`xDU4p3bk-YPOk{iOlET3KKZ70oFM+n$jY?S3sG0H zu*WU8SR^& zR!ZcM21U&ABzB{P39H?Jz0hvR;Dme3=H=oQb_`iYIUARS1xRpaj3htF%lI{zqy0QV z0vSZ69Cm57=R%!_tR>n*+TMUZV6mtwv0?3~y?}M+K-+`STzkjtJn+0e96b-)C$tPn z%ZQmd5b_JNa6i=~#REI(iwDN7L#Rv~yE zma5P-t&f2lmSt_*^{`zFYb&*=tu)v(B3}m%o9AL7VPF6N+j*o*Y#0Fe40bR8-V8Rx zT&+YVBQ5L_e1M<95(ebe%#EbFmsE5EHbI)qJW)vc0D;0#h)mRy4#0p)q9`lc3CVd& z9ARc^&;sDLjU)DW=K5!WjE~M9oSC|)zh>kS~ zUjr#_r$0 z&w3yBV*CIUVrf;b_*qHshY7XfRK&91 zuAj)ISgW$y+1aPQTk?6+Aa@c|X$;x|Yus*A7~@Qt_i~xSWv!a2+ue8S!3pae9UXCc zU-NNeDn$TxDS2~{INm$7fw4{^QyxXCP&SIX=uj!iHo|xl>|W>nmV|NNPJdq#>N=#N zPwHk%VPyAca(40b=+Wfzd~!K?`SpXXMft|QtVCfH6=Gxd5r0T0{@QxuI=0EG5IMKk zn|WFn&Bw!EK50O?)!X*-^CI34rBT-M{#{-bH~rdo2>k!%5S^XVWG#wVzQd)L;PNf| z9gbDf)>Xk9NHb}#RJIb`9fT=BHp?w`E0bM2dYU8+tblEY75{dr;w4vx=Rj{M6aK0; zmM<{PWi#eKpec+YdDU?OXAt_{}y;LUet9 Z>koWxHVtp~?C!@z`Ut-Y&rcbx{shVN^>qLM literal 0 HcmV?d00001 diff --git a/LibraryTree.py b/LibraryTree.py index fb02ad19..2cbc2d6e 100644 --- a/LibraryTree.py +++ b/LibraryTree.py @@ -1161,6 +1161,11 @@ def OnItemRename(self, evt): ### extract behavioral python file (from .amd or .cmd) to tempdir ### in order to rename it and change the name of contening class temp_file = None + temp_dat_file = None + + print(Zip.CheckDatFile(old_path)) + + return with zipfile.ZipFile(old_path) as zf: ### find all python files for file in zf.namelist(): @@ -1188,18 +1193,22 @@ def OnItemRename(self, evt): os.rename(temp_file, new_temp_file) elif file.endswith(".dat"): - import pickle + ### pickle object is not modifiable ! + pass + + #import pickle ### replace in new_temp_file file - temp_dat_file = zf.extract(file,tempfile.gettempdir()) + #temp_dat_file = zf.extract(file,tempfile.gettempdir()) - with open(temp_dat_file, 'rb') as sf: - scores = pickle.load(sf) - - scores[0] = new_filepath - scores[1] = os.path.join(new_filepath,os.path.basename(new_filepath).replace('.amd','.py').replace('.cmd','.py')) + #with open(temp_dat_file, 'rb') as sf: + # block = pickle.load(sf) + + #block.obj.model_path = new_filepath + #block.obj.python_path = os.path.join(new_filepath,os.path.basename(new_filepath).replace('.amd','.py').replace('.cmd','.py')) - with open(temp_dat_file, "wb") as sf: - pickle.dump(scores, sf) + #print(block) + + #pickle.dump(obj = block, file = open(temp_dat_file, "wb"), protocol = 0) if temp_file: @@ -1214,7 +1223,7 @@ def OnItemRename(self, evt): print("Delete %s"%os.path.basename(temp_file)) print("Update %s"%new_temp_file) - zip.Update([new_temp_file,temp_dat_file]) + zip.Update([new_temp_file]) print("rename %s to %s"%(old_path,new_filepath)) diff --git a/Mixins/Savable.py b/Mixins/Savable.py index 75ab4da0..96796854 100644 --- a/Mixins/Savable.py +++ b/Mixins/Savable.py @@ -66,7 +66,7 @@ class PickledCollection(list): """ def __init__(self, obj): - """ Constructor + """ Constructor. """ self.obj = obj @@ -148,83 +148,84 @@ class DumpZipFile(DumpBase): ext = [".amd", ".cmd"] def Save(self, obj_dumped, fileName = None): - """ Function that save the codeblock on the disk. + """ Function that save the codeblock on the disk. """ - assert(fileName.endswith(tuple(DumpZipFile.ext))) + assert(fileName.endswith(tuple(DumpZipFile.ext))) - ### local copy of paths - python_path = obj_dumped.python_path - image_path = obj_dumped.image_path + ### local copy of paths + python_path = obj_dumped.python_path + image_path = obj_dumped.image_path - ### Now, file paths are in the compressed file - if os.path.isabs(python_path): - path = os.path.join(fileName, os.path.basename(obj_dumped.python_path)) - if os.path.exists(path): - obj_dumped.python_path = path + ### Now, file paths are in the compressed file + if os.path.isabs(python_path): + path = os.path.join(fileName, os.path.basename(obj_dumped.python_path)) + if os.path.exists(path): + obj_dumped.python_path = path - if os.path.isabs(image_path): - obj_dumped.image_path = os.path.join(fileName, os.path.basename(obj_dumped.image_path)) + if os.path.isabs(image_path): + obj_dumped.image_path = os.path.join(fileName, os.path.basename(obj_dumped.image_path)) - obj_dumped.model_path = fileName + obj_dumped.model_path = fileName - ### args is constructor args and we save these and not the current value - if hasattr(obj_dumped, 'args'): - obj_dumped.args = Components.GetArgs(Components.GetClass(obj_dumped.python_path)) - try: + ### args is constructor args and we save these and not the current value + if hasattr(obj_dumped, 'args'): + obj_dumped.args = Components.GetArgs(Components.GetClass(obj_dumped.python_path)) - fn = 'DEVSimPyModel.dat' + try: + + fn = 'DEVSimPyModel.dat' - ### dump attributes in fn file - pickle.dump( obj = PickledCollection(obj_dumped), - file = open(fn, "wb"), - protocol = 0) + ### dump attributes in fn file + pickle.dump( obj = PickledCollection(obj_dumped), + file = open(fn, "wb"), + protocol = 0) - except Exception as info: - tb = traceback.format_exc() - sys.stderr.write(_("Problem saving (during the dump): %s -- %s\n")%(str(fileName),str(tb))) - return False - else: + except Exception as info: + tb = traceback.format_exc() + sys.stderr.write(_("Problem saving (during the dump): %s -- %s\n")%(str(fileName),str(tb))) + return False + else: - try: + try: - zf = ZipManager.Zip(fileName) + zf = ZipManager.Zip(fileName) - ### create or update fileName - if os.path.exists(fileName): - zf.Update(replace_files = [fn, python_path, image_path]) - else: - zf.Create(add_files = [fn, python_path, image_path]) + ### create or update fileName + if os.path.exists(fileName): + zf.Update(replace_files = [fn, python_path, image_path]) + else: + zf.Create(add_files = [fn, python_path, image_path]) - os.remove(fn) + os.remove(fn) - ## abs path of the directory that contains the file to export (str() to avoid unicode) - newExportPath = str(os.path.dirname(fileName)) + ## abs path of the directory that contains the file to export (str() to avoid unicode) + newExportPath = str(os.path.dirname(fileName)) - mainW = getTopLevelWindow() - ### if export on local directory, we insert the path in the config file - if os.path.basename(DOMAIN_PATH) not in newExportPath.split( - os.sep - ): - ### update of .devsimpy config file - mainW.exportPathsList = eval(mainW.cfg.Read("exportPathsList")) - if newExportPath not in mainW.exportPathsList: - mainW.exportPathsList.append(str(newExportPath)) - mainW.cfg.Write("exportPathsList", str(eval("mainW.exportPathsList"))) + mainW = getTopLevelWindow() + ### if export on local directory, we insert the path in the config file + if os.path.basename(DOMAIN_PATH) not in newExportPath.split( + os.sep + ): + ### update of .devsimpy config file + mainW.exportPathsList = eval(mainW.cfg.Read("exportPathsList")) + if newExportPath not in mainW.exportPathsList: + mainW.exportPathsList.append(str(newExportPath)) + mainW.cfg.Write("exportPathsList", str(eval("mainW.exportPathsList"))) - ### if lib is already in the lib tree, we update the tree - mainW.tree.UpdateDomain(newExportPath) - ### to sort lib tree - mainW.tree.SortChildren(mainW.tree.root) + ### if lib is already in the lib tree, we update the tree + mainW.tree.UpdateDomain(newExportPath) + ### to sort lib tree + mainW.tree.SortChildren(mainW.tree.root) - except Exception as info: - tb = traceback.format_exc() - NotificationMessage(_('Error'), _("Problem saving (during the zip handling): %s -- %s\n")%(str(fileName),info), parent=getTopLevelWindow(), timeout=5) - sys.stderr.write(_("Problem saving (during the zip handling): %s -- %s\n")%(str(fileName),str(tb))) + except Exception as info: + tb = traceback.format_exc() + NotificationMessage(_('Error'), _("Problem saving (during the zip handling): %s -- %s\n")%(str(fileName),info), parent=getTopLevelWindow(), timeout=5) + sys.stderr.write(_("Problem saving (during the zip handling): %s -- %s\n")%(str(fileName),str(tb))) - return False - else: + return False + else: - return True + return True def Load(self, obj_loaded, fileName = None): """ Load codeblock (obj_loaded) from fileName @@ -306,17 +307,22 @@ def Load(self, obj_loaded, fileName = None): else: setattr(obj_loaded, attr, L[i]) - except IndexError as info: tb = traceback.format_exc() sys.stderr.write(_("Problem loading (old model): %s -- %s \n")%(str(fileName), str(tb))) return info - ### if the model was made from another pc if not os.path.exists(obj_loaded.model_path): obj_loaded.model_path = fileName + #with zipfile.ZipFile(fileName) as zf: + # ### find all python files + # for file in zf.namelist(): + # r = repr(zf.read(file)) + # if file.endswith(".py") and ('DomainBehavior' in r or 'DomainStructure' in r): + # obj_loaded.python_path = os.path.join(obj_loaded.model_path, re.findall(".*(?:.[a|c]md)+[/|\\\](.*.py)*", obj_loaded.python_path)[-1]) + ### if python file is wrong if not os.path.exists(os.path.dirname(obj_loaded.python_path)): ### ?: for exclude or non-capturing rule diff --git a/ZipManager.py b/ZipManager.py index ff017fc8..8d8e1809 100644 --- a/ZipManager.py +++ b/ZipManager.py @@ -20,7 +20,9 @@ import inspect import types import importlib - +import pickle +import tempfile + import gettext _ = gettext.gettext @@ -217,6 +219,55 @@ def Delete(self, delete_files:[str]=[])->None: return del_flag + @staticmethod + def CheckBehavioralPythonFile(fn:str)->bool: + """ Check if the behavioral python file is conform. + """ + if not zipfile.is_zipfile(fn): + return False + + bn = os.path.basename(fn) + + with zipfile.ZipFile(fn) as zf: + ### find all python files + for file in zf.namelist(): + r = repr(zf.read(file)) + if file.endswith(".py") and ('class %s(DomainBehavior):'%(bn) in r or 'class s%(DomainStructure)'%(bn) in r): + return True + + return False + + @staticmethod + def CheckDatFile(fn:str)->bool: + """ Check if the DEVSimPyModel.dat has a goog model and python filename. + """ + + file = Zip.GetDatFile(fn) + + if file != "": + with open(file, 'rb') as sf: + block = pickle.load(sf) + + return block[0] == fn and \ + block[1] == os.path.join(fn,os.path.basename(fn).replace('.amd','.py').replace('.cmd','.py')) + else: + return False + + @staticmethod + def GetDatFile(fn:str)->str: + """ Return the DEVSimPyModel.dat file. + """ + + if not zipfile.is_zipfile(fn): + return "" + + with zipfile.ZipFile(fn) as zf: + ### find all python files + for file in zf.namelist(): + if file.endswith(".dat"): + return zf.extract(file,tempfile.gettempdir()) + return "" + def GetImage(self, scaleW:int=16, scaleH:int=16): """ Get image object from image file stored in zip file. scaleH and scaleW are used to rescale image @@ -249,6 +300,10 @@ def GetImage(self, scaleW:int=16, scaleH:int=16): def GetBehavioralPythonFile(fn:str)->str: """ TODO: comment """ + + if not zipfile.is_zipfile(fn): + return "" + ### zipfile (amd or cmd) zf = zipfile.ZipFile(fn, 'r') nl = zf.namelist() @@ -259,7 +314,8 @@ def GetBehavioralPythonFile(fn:str)->str: for s in [s for s in nl if s.endswith(".py")]: n, e = os.path.splitext(s) - if n == name: + r = repr(zf.read(s)) + if n == name and 'DomainBehavior' in r or 'DomainStructure' in r: return s return "" @@ -268,6 +324,10 @@ def GetBehavioralPythonFile(fn:str)->str: def GetPluginFile(fn:str)->str: """ TODO: comment """ + + if not zipfile.is_zipfile(fn): + return "" + ### zipfile (amd or cmd) zf = zipfile.ZipFile(fn, 'r') nl = zf.namelist() @@ -281,6 +341,9 @@ def HasPlugin(fn:str)->bool: """ TODO: comment """ + if not zipfile.is_zipfile(fn): + return "" + ### zipfile (amd or cmd) zf = zipfile.ZipFile(fn, 'r') nl = zf.namelist() From d4df2b2c49f29f2fd520575e21864ff8a3b7b73a Mon Sep 17 00:00:00 2001 From: Capocchi L Date: Sat, 7 Nov 2020 18:18:33 +0100 Subject: [PATCH 2/3] Rename of model from lib is implemented --- Components.py | 175 ++++++++++++++++++++++++++++++++++++++-------- Container.py | 22 +++--- LibraryTree.py | 150 ++++++--------------------------------- Mixins/Savable.py | 76 ++++++++++---------- ZipManager.py | 40 ++--------- 5 files changed, 220 insertions(+), 243 deletions(-) diff --git a/Components.py b/Components.py index 6a606e14..b092d3bc 100644 --- a/Components.py +++ b/Components.py @@ -33,6 +33,7 @@ import types import importlib import subprocess +import tempfile import gettext _ = gettext.gettext @@ -48,7 +49,7 @@ import ZipManager -from Utilities import GetActiveWindow, path_to_module, install_and_import, printOnStatusBar +from Utilities import replaceAll, GetActiveWindow, path_to_module, install_and_import, printOnStatusBar from NetManager import Net from SimpleFrameEditor import FrameEditor from which import which @@ -167,7 +168,50 @@ class PyComponent: """ @staticmethod - def Load(filename, label): + def Rename(filename:str, new_name:str): + """ Rename the filename with the new_name. + """ + + old_bn = os.path.basename(filename) + dn = os.path.dirname(filename) + old_name, ext = os.path.splitext(old_bn) + + new_filepath = "".join([os.path.join(dn, new_name),ext]) + + #read input file + fin = open(filename, "rt") + #read file contents to string + data = fin.read() + + if 'class %s(DomainBehavior):'%old_name in data or 'class %s(DomainStructure):'%old_name: + + #replace all occurrences of the required string + data = data.replace(old_name, new_name) + #close the input file + fin.close() + + #overrite the input file with the resulting data + with open(filename, "wt") as fin: + fin.write(data) + + ### relace on file system + os.rename(filename, new_filepath) + + ### replace in __init__.py file if exist! + init_file = os.path.join(dn,'__init__.py') + if os.path.isfile(init_file): + replaceAll(init_file, old_name, new_name) + + return True + else: + info = _("It seams that the python file dont inherite of the DomainBehavior or DomainStructure classes or \ + its name and the name of the class is different.\n \ + Please correct this aspect before wanted to rename the python file from DEVSimPy.") + wx.MessageBox(info, _("Error"), wx.OK|wx.ICON_ERROR) + return False + + @staticmethod + def Load(filename:str, label:str): """ Load python file from filename """ fn = filename.strip() @@ -183,7 +227,7 @@ class GenericComponent: """ """ def __init__(self, *argv, **kwargs): - """ + """ Constructor. """ # local copy self._canvas = kwargs['canvas'] if 'id' in kwargs else None @@ -215,13 +259,13 @@ def __init__(self, *argv, **kwargs): self._specific_behavior = kwargs.get('specific_behavior','') def Create(self): - """ Create component from attributes + """ Abstract method to create component from attributes. """ pass @staticmethod def Load(filename, label, x, y, canvas): - """ Load stored component form filename + """ Abstract method to load stored component form filename. """ pass @@ -248,24 +292,86 @@ def ChekFilename(filename, model): return model + @staticmethod + def Rename(filename:str, new_name:str)->bool: + """ Rename the filename with the new_name. + """ -class CMDComponent(GenericComponent): - """ Return labeled block from filename at (x,y) position in canvas + + old_bn = os.path.basename(filename) + dn = os.path.dirname(filename) + old_name, ext = os.path.splitext(old_bn) + + new_filepath = "".join([os.path.join(dn, new_name), ext]) + + ### extract behavioral python file (from .amd or .cmd) to tempdir + ### in order to rename it and change the name of contening class + temp_file = None + + with zipfile.ZipFile(filename) as zf: + ### find all python files + for file in zf.namelist(): + if file.endswith(".py"): + r = repr(zf.read(file)) + ### first find python file with the same of the archive + if file.endswith(old_bn): + #new_bn = os.path.basename(new_filepath) + temp_file = zf.extract(old_bn, tempfile.gettempdir()) + new_temp_file = temp_file + + ### then find a python file that inherite of the DomainBehavior or StructureBehavior class + elif 'DomainBehavior' in r or 'DomainStructure' in r: + + old_name = os.path.splitext(file)[0] + + ### first we must change the name of this python file in order to have the same as the archive! + temp_file = zf.extract(file, tempfile.gettempdir()) + new_temp_file = os.path.join(tempfile.gettempdir(), new_name+'.py') + ### rename temp_file to new_temp_file according to the correspondance between the name of the python file and the name of the archive + ### for exemple C:\Users\Laurent\AppData\Local\Temp\MyOld.py C:\Users\Laurent\AppData\Local\Temp\MyNew.py + if os.path.isfile(new_temp_file): + os.remove(new_temp_file) + + os.rename(temp_file, new_temp_file) + + if temp_file: + + print("Replace %s by %s into %s"%(old_name, new_name, new_temp_file)) + ### replace in new_temp_file file + replaceAll(new_temp_file, old_name, new_name) + + zip = ZipManager.Zip(filename) + + if zip.Delete([os.path.basename(temp_file)]): + print("Delete %s"%os.path.basename(temp_file)) + + print("Update %s"%new_temp_file) + zip.Update([new_temp_file]) - @filename: filename for loading block - @label: label of block - @x: horizontal position - @y: vertical position - @canvas: canvas accepting block + print("rename %s to %s"%(filename,new_filepath)) + + ### relace on file system + os.rename(filename, new_filepath) + + return True + + else: + info = _("It seams that the python filename and the model name are diffrent!\n \ + Please correct this aspect by extracting the archive.") + wx.MessageBox(info, _("Error"), wx.OK|wx.ICON_ERROR) + return False + +class CMDComponent(GenericComponent): + """ """ def __init__(self, *argv, **kwargs): - """ Constructor + """ Constructor. """ GenericComponent.__init__(self, *argv, **kwargs) def Create(self): - """ Create CMD from constructor + """ Create CMD from constructor. """ from Container import ContainerBlock, iPort, oPort # new containerBlock model @@ -293,7 +399,7 @@ def Create(self): @staticmethod def Load(filename, label): - """ Load CMD from filename + """ Load CMD from filename. """ from Container import ContainerBlock, iPort, oPort assert(filename.endswith('.cmd')) @@ -322,13 +428,7 @@ def Load(filename, label): return CMDComponent.ChekFilename(filename, m) class AMDComponent(GenericComponent): - """ Return labeled block from filename at (x,y) position in canvas - - @filename: filename for loading block - @label: label of block - @x: horizontal position - @y: vertical position - @canvas: canvas accepting block + """ """ def __init__(self, *argv, **kwargs): @@ -337,7 +437,7 @@ def __init__(self, *argv, **kwargs): GenericComponent.__init__(self, *argv, **kwargs) def Create(self): - """ Create AMD from filename + """ Create AMD from filename. """ # associated Python class @@ -431,11 +531,11 @@ def BlockModelAdapter(cls, label="", specific_behavior=""): #--------------------------------------------------------- class DEVSComponent: - """ Editable class + """ """ def __init__(self): - """ Constructor of DEVSComponent. + """ Constructor. """ # DEVS instance @@ -483,11 +583,11 @@ def debugger(m, msg): except Exception: f.write("clock %d: %s\n"%(0.0, str(msg))) - def setDEVSPythonPath(self, python_path): + def setDEVSPythonPath(self, python_path:str): if os.path.isfile(python_path) or zipfile.is_zipfile(os.path.dirname(python_path)): self.python_path = python_path - def getDEVSPythonPath(self): + def getDEVSPythonPath(self)->str: """ Return the DEVS python path. """ return self.python_path @@ -508,17 +608,32 @@ def setDEVSModel(self, devs): self.setBlock(devs) def setDEVSParent(self, p): + """ + Set the DEVS parent model. + + @param p: parent + @type: instance + """ if self.devsModel != None: self.devsModel.parent = p def getDEVSParent(self): + """ + Get the DEVS parent. + """ return self.devsModel.parent if self.devsModel else None def getBlock(self): + """ + Get the Block. + """ if self.devsModel is not None: return DEVSComponent.getBlockModel(self.devsModel) def setBlock(self, devs): + """ + Set the Block. + """ if devs is not None: ### define new methods in order to set and get blockModel from devs instance if not hasattr(devs, 'getBlockModel'): @@ -573,7 +688,7 @@ def OnLog(self, event): dial.ShowModal() def updateDEVSPriorityList(self): - """ update the componentSet order from priority_list for corresponding diagram + """ Update the componentSet order from priority_list for corresponding diagram """ from Container import ContainerBlock, Diagram, Block assert(isinstance(self, (ContainerBlock, Diagram))) @@ -598,7 +713,7 @@ def updateDEVSPriorityList(self): ### def OnEditor(self, event): - """ Method that edit the python code of associated devs model of the Block + """ Method that edit the python code of associated devs model of the Block. """ from Container import ShapeCanvas @@ -702,7 +817,7 @@ def OnEditor(self, event): return False class BlockFactory: - """ DEVSimPy Block Factory + """ DEVSimPy Block Factory. """ @staticmethod diff --git a/Container.py b/Container.py index ff42f728..b1a30384 100644 --- a/Container.py +++ b/Container.py @@ -3436,7 +3436,8 @@ def OnExport(self, event): ### Export by using right clic menu if isinstance(menu, wx.Menu): menuItem = menu.FindItemById(itemId) - ext = menuItem.GetLabel().lower() + ext = menuItem.GetItemLabel().lower() + ### export (save) by using save button of DetachedFrame else: ext = 'cmd' @@ -3475,7 +3476,7 @@ def OnExport(self, event): dlg.ShowModal() def update(self, concret_subject = None): - """ Update method to respond to notify call + """ Update method to respond to notify call. """ state = concret_subject.GetState() @@ -3526,7 +3527,7 @@ def update(self, concret_subject = None): def __repr__(self): """ """ - s = _("\t Label: %s\n")%self.label + s = _("\n\t Label: %s\n")%self.label s += _("\t Input/Output: %s,%s\n")%(str(self.input), str(self.output)) return s @@ -3776,7 +3777,8 @@ def __getattr__(self, name): """Called when an attribute lookup has not found the attribute in the usual places """ if name == 'dump_attributes': - return ['model_path', 'python_path', 'args'] + self.GetAttributes() + #return ['model_path', 'python_path', 'args'] + self.GetAttributes() + return ['args'] + self.GetAttributes() #======================================================================= elif name == 'dump_abstr_attributes': ### Atomic model has no abstract attributes @@ -3853,9 +3855,9 @@ def __repr__(self): """ Text representation. """ s = Block.__repr__(self) - s+= _("\t DEVS module path: %s \n")%str(self.python_path) - s+= _("\t DEVSimPy model path: %s \n")%str(self.model_path) - s+= _("\t DEVSimPy image path: %s \n")%str(self.image_path) + s+= _("\t DEVS module path: %s\n")%str(self.python_path) + s+= _("\t DEVSimPy model path: %s\n")%str(self.model_path) + s+= _("\t DEVSimPy image path: %s\n")%str(self.image_path) return s #--------------------------------------------------------- @@ -4020,9 +4022,9 @@ def OnLeftDClick(self, event): def __repr__(self): s = Block.__repr__(self) - s += _("\t DEVS module path: %s \n"%str(self.python_path)) - s += _("\t DEVSimPy model path: %s \n")%str(self.model_path) - s +=_("\t DEVSimPy image path: %s \n")%str(self.image_path) + s += _("\t DEVS module path: %s\n"%str(self.python_path)) + s += _("\t DEVSimPy model path: %s\n")%str(self.model_path) + s +=_("\t DEVSimPy image path: %s\n")%str(self.image_path) return s #--------------------------------------------------------- diff --git a/LibraryTree.py b/LibraryTree.py index 2cbc2d6e..a9262a97 100644 --- a/LibraryTree.py +++ b/LibraryTree.py @@ -37,7 +37,7 @@ from Utilities import replaceAll, getPYFileListFromInit, path_to_module, printOnStatusBar, NotificationMessage, install_and_import, module_list from Decorators import BuzyCursorNotification -from Components import BlockFactory, DEVSComponent, GetClass +from Components import BlockFactory, DEVSComponent, GetClass, PyComponent, GenericComponent from ZipManager import Zip, getPythonModelFileName from ReloadModule import recompile from ImportLibrary import DeleteBox @@ -737,7 +737,7 @@ def InsertNewDomain(self, dName, parent, L = []): module = load_module_from_net(path) ### check error - error = isinstance(module, Exception) + error = isinstance(module, Exception) ### change icon depending on the error and the presence of image in amd if error: @@ -961,7 +961,7 @@ def UpdateDomain(self, path): @BuzyCursorNotification def OnUpdateAll(self, event): - """ Update all imported domain + """ Update all imported domain. """ result = self.UpdateAll() if len(result) == 0: @@ -1106,144 +1106,34 @@ def OnItemRename(self, evt): ### dialog to ask new label if wx.VERSION_STRING < '4.0': - d = wx.TextEntryDialog(self, _('New file name'), defaultValue = name, style=wx.OK) + d = wx.TextEntryDialog(self, _('New file name:'), defaultValue = name, style=wx.OK) else: - d = wx.TextEntryDialog(self, _('New file name'), value = name, style=wx.OK) + d = wx.TextEntryDialog(self, _('New file name:'), value = name, style=wx.OK) d.ShowModal() ### new label new_label = d.GetValue() - ### if new and old label are different + + ### only if new and old label are different if new_label != name: ### path of file - old_path = self.GetItemPyData(item) + filename = self.GetItemPyData(item) - old_bn = os.path.basename(old_path) - dn = os.path.dirname(old_path) - old_name, ext = os.path.splitext(old_bn) - - new_filepath = "".join([os.path.join(dn, new_label),ext]) - - if old_path.endswith('.py'): - - #read input file - fin = open(old_path, "rt") - #read file contents to string - data = fin.read() - - if 'DomainBehavior' in data or 'DomainStructure' in data: - - #replace all occurrences of the required string - data = data.replace(old_name, new_label) - #close the input file - fin.close() - - #open the input file in write mode - fin = open(old_path, "wt") - #overrite the input file with the resulting data - fin.write(data) - #close the file - fin.close() - - ### relace on file system - os.rename(old_path, new_filepath) - - ### replace in __init__.py file - replaceAll(os.path.join(dn,'__init__.py'), old_name, new_label) - else: - wx.MessageBox(_("It seams that the python file dont inherite of the DomainBehavior or DomainStructure classes.\n \ - Please correct this aspect before wanted to rename the python file from DEVSimPy."), _("Error"), wx.OK|wx.ICON_ERROR) - return - - ### if devsimpy model - elif zipfile.is_zipfile(old_path): - ### extract behavioral python file (from .amd or .cmd) to tempdir - ### in order to rename it and change the name of contening class - temp_file = None - temp_dat_file = None - - print(Zip.CheckDatFile(old_path)) - - return - with zipfile.ZipFile(old_path) as zf: - ### find all python files - for file in zf.namelist(): - if file.endswith(".py"): - r = repr(zf.read(file)) - ### first find python file with the same of the archive - if file.endswith(old_bn): - #new_bn = os.path.basename(new_filepath) - temp_file = zf.extract(old_bn,tempfile.gettempdir()) - new_temp_file = temp_file - - ### then find a python file that inherite of the DomainBehavior or StructureBehavior class - elif 'DomainBehavior' in r or 'DomainStructure' in r: - - old_name = os.path.splitext(file)[0] - - ### first we must change the name of this python file in order to have the same as the archive! - temp_file = zf.extract(file,tempfile.gettempdir()) - new_temp_file = os.path.join(tempfile.gettempdir(),new_label+'.py') - ### rename temp_file to new_temp_file according to the correspondance between the name of the python file and the name of the archive - ### for exemple C:\Users\Laurent\AppData\Local\Temp\MyOld.py C:\Users\Laurent\AppData\Local\Temp\MyNew.py - if os.path.isfile(new_temp_file): - os.remove(new_temp_file) - - os.rename(temp_file, new_temp_file) - - elif file.endswith(".dat"): - ### pickle object is not modifiable ! - pass - - #import pickle - ### replace in new_temp_file file - #temp_dat_file = zf.extract(file,tempfile.gettempdir()) - - #with open(temp_dat_file, 'rb') as sf: - # block = pickle.load(sf) - - #block.obj.model_path = new_filepath - #block.obj.python_path = os.path.join(new_filepath,os.path.basename(new_filepath).replace('.amd','.py').replace('.cmd','.py')) - - #print(block) - - #pickle.dump(obj = block, file = open(temp_dat_file, "wb"), protocol = 0) - - if temp_file: - - print("Replace %s by %s into %s"%(old_name,new_label,new_temp_file)) - ### replace in new_temp_file file - replaceAll(new_temp_file, old_name, new_label) - - print("open %s"%old_path) - zip = Zip(old_path) - - if zip.Delete([os.path.basename(temp_file)]): - print("Delete %s"%os.path.basename(temp_file)) - - print("Update %s"%new_temp_file) - zip.Update([new_temp_file]) - - print("rename %s to %s"%(old_path,new_filepath)) - - ### relace on file system - os.rename(old_path, new_filepath) - - #try: - # os.remove(temp_file) - #except: - # pass - - else: - wx.MessageBox(_("It seams that the python filename and the model name are diffrent!\n \ - Please correct this aspect by extracting the archive."), _("Error"), wx.OK|wx.ICON_ERROR) - return + ### if pure python file + if filename.endswith('.py'): + cls = PyComponent + ### if .amd or .cmd file + elif zipfile.is_zipfile(filename): + cls = GenericComponent else: + cls = None + + ### if cls (.py, .cmd or .amd) and Rename is ok, we updateAll lib + if cls and not cls.Rename(filename, new_label): sys.stdout.write(_('Rename failed!')) - return - - self.UpdateAll() + else: + self.UpdateAll() ### def OnItemDocumentation(self, evt): diff --git a/Mixins/Savable.py b/Mixins/Savable.py index 96796854..d8bddebd 100644 --- a/Mixins/Savable.py +++ b/Mixins/Savable.py @@ -62,19 +62,17 @@ import ZipManager class PickledCollection(list): - """ Custom list class for dsp attributes dumping + """ Custom list class for dsp attributes dumping. """ def __init__(self, obj): """ Constructor. """ - self.obj = obj - - self.pickled_obj = [getattr(self.obj, attr) for attr in self.obj.dump_attributes] + self.pickled_obj = [getattr(obj, attr) for attr in obj.dump_attributes] #======================================================================= ### addition of abstraction attributes only if there is not in dump_attributes (after having saved a model, dump_attributes contains abstraction attributes !) - self.pickled_obj += [getattr(self.obj, attr) for attr in self.obj.dump_abstr_attributes if attr not in self.obj.dump_attributes] + self.pickled_obj += [getattr(obj, attr) for attr in obj.dump_abstr_attributes if attr not in obj.dump_attributes] #======================================================================= def __setstate__(self, state): @@ -83,10 +81,9 @@ def __setstate__(self, state): self.__dict__.update(state) def __iter__(self): - """ Overwrite iterator protocol + """ Overwrite iterator protocol. """ - for v in self.pickled_obj: - yield v + yield from self.pickled_obj class DumpBase(object): """ DumpBase class @@ -100,17 +97,17 @@ class DumpBase(object): ### extension is in whiteList @staticmethod - def GetExt(fileName=""): + def GetExt(fileName:str=""): ext = os.path.splitext(fileName)[-1] if ext in DumpBase.WhiteList: return ext else: - sys.stdout.write(_("\nThis extension is unknown: %s")%ext) + sys.stdout.write(_("\nThis extension is unknown: %s.")%ext) return False ### Return the class in charge of saving or loading from ext of object. @staticmethod - def GetAssociateCls(ext=""): + def GetAssociateCls(ext:str=""): try: return DumpBase.DB[ext] except KeyError: @@ -121,7 +118,7 @@ def GetAssociateCls(ext=""): ### et on ajoute au dictionnaire DumpBase.vars en cl� l'extension ext et en valeur le nom de la classe correspondante @staticmethod def PopulateDB(): - """ + """ Polpulate. """ subclasses = itersubclasses(DumpBase) @@ -133,11 +130,13 @@ def PopulateDB(): DumpBase.DB[elem] = cls def Load(self, filename): - """Retrieve data from the file source.""" + """Retrieve data from the file source. + """ pass def Save(self, filename): - """Save the data object to the file.""" + """Save the data object to the file. + """ pass ###----------------------------------------------------------- @@ -147,25 +146,21 @@ class DumpZipFile(DumpBase): ext = [".amd", ".cmd"] - def Save(self, obj_dumped, fileName = None): + def Save(self, obj_dumped, fileName = None)->bool: """ Function that save the codeblock on the disk. """ assert(fileName.endswith(tuple(DumpZipFile.ext))) - ### local copy of paths - python_path = obj_dumped.python_path - image_path = obj_dumped.image_path - ### Now, file paths are in the compressed file - if os.path.isabs(python_path): - path = os.path.join(fileName, os.path.basename(obj_dumped.python_path)) - if os.path.exists(path): - obj_dumped.python_path = path + #if os.path.isabs(python_path): + # path = os.path.join(fileName, os.path.basename(obj_dumped.python_path)) + # if os.path.exists(path): + # obj_dumped.python_path = path - if os.path.isabs(image_path): - obj_dumped.image_path = os.path.join(fileName, os.path.basename(obj_dumped.image_path)) + #if os.path.isabs(image_path): + # obj_dumped.image_path = os.path.join(fileName, os.path.basename(obj_dumped.image_path)) - obj_dumped.model_path = fileName + #obj_dumped.model_path = fileName ### args is constructor args and we save these and not the current value if hasattr(obj_dumped, 'args'): @@ -190,6 +185,10 @@ def Save(self, obj_dumped, fileName = None): zf = ZipManager.Zip(fileName) + ### local copy of paths + python_path = obj_dumped.python_path + image_path = obj_dumped.image_path + ### create or update fileName if os.path.exists(fileName): zf.Update(replace_files = [fn, python_path, image_path]) @@ -203,9 +202,7 @@ def Save(self, obj_dumped, fileName = None): mainW = getTopLevelWindow() ### if export on local directory, we insert the path in the config file - if os.path.basename(DOMAIN_PATH) not in newExportPath.split( - os.sep - ): + if os.path.basename(DOMAIN_PATH) not in newExportPath.split(os.sep): ### update of .devsimpy config file mainW.exportPathsList = eval(mainW.cfg.Read("exportPathsList")) if newExportPath not in mainW.exportPathsList: @@ -221,10 +218,8 @@ def Save(self, obj_dumped, fileName = None): tb = traceback.format_exc() NotificationMessage(_('Error'), _("Problem saving (during the zip handling): %s -- %s\n")%(str(fileName),info), parent=getTopLevelWindow(), timeout=5) sys.stderr.write(_("Problem saving (during the zip handling): %s -- %s\n")%(str(fileName),str(tb))) - return False else: - return True def Load(self, obj_loaded, fileName = None): @@ -313,8 +308,8 @@ def Load(self, obj_loaded, fileName = None): return info ### if the model was made from another pc - if not os.path.exists(obj_loaded.model_path): - obj_loaded.model_path = fileName + #if not os.path.exists(obj_loaded.model_path): + obj_loaded.model_path = fileName #with zipfile.ZipFile(fileName) as zf: # ### find all python files @@ -324,9 +319,11 @@ def Load(self, obj_loaded, fileName = None): # obj_loaded.python_path = os.path.join(obj_loaded.model_path, re.findall(".*(?:.[a|c]md)+[/|\\\](.*.py)*", obj_loaded.python_path)[-1]) ### if python file is wrong - if not os.path.exists(os.path.dirname(obj_loaded.python_path)): + #if not os.path.exists(os.path.dirname(obj_loaded.python_path)): ### ?: for exclude or non-capturing rule - obj_loaded.python_path = os.path.join(obj_loaded.model_path, re.findall(".*(?:.[a|c]md)+[/|\\\](.*.py)*", obj_loaded.python_path)[-1]) + #obj_loaded.python_path = os.path.join(obj_loaded.model_path, re.findall(".*(?:.[a|c]md)+[/|\\\](.*.py)*", obj_loaded.python_path)[-1]) + + obj_loaded.python_path = os.path.join(fileName,os.path.basename(fileName).replace('.cmd','.py').replace('.amd','.py')) return True @@ -386,7 +383,7 @@ class DumpGZipFile(DumpBase): """ ext = [".dsp"] - def Save(self, obj_dumped, fileName = None): + def Save(self, obj_dumped, fileName=None): """ Function that save the dump on the disk under filename. """ @@ -401,14 +398,15 @@ def Save(self, obj_dumped, fileName = None): tb = traceback.format_exc() sys.stderr.write(_("\nProblem saving: %s -- %s\n")%(str(fileName),str(tb))) return False + else: return True - def Load(self, obj_loaded, fileName = None): - """ Function that save the dump on the disk with the filename. + def Load(self, obj_loaded, fileName=None): + """ Function that load the diagram from its filename. """ - # try to open f with compressed mode + # try to open filename with compressed mode try: f = gzip.GzipFile(filename = fileName, mode='rb') f.read(1) # trigger an exception if is not compressed diff --git a/ZipManager.py b/ZipManager.py index 8d8e1809..ac824712 100644 --- a/ZipManager.py +++ b/ZipManager.py @@ -220,38 +220,9 @@ def Delete(self, delete_files:[str]=[])->None: return del_flag @staticmethod - def CheckBehavioralPythonFile(fn:str)->bool: - """ Check if the behavioral python file is conform. - """ - if not zipfile.is_zipfile(fn): - return False - - bn = os.path.basename(fn) - - with zipfile.ZipFile(fn) as zf: - ### find all python files - for file in zf.namelist(): - r = repr(zf.read(file)) - if file.endswith(".py") and ('class %s(DomainBehavior):'%(bn) in r or 'class s%(DomainStructure)'%(bn) in r): - return True - - return False - - @staticmethod - def CheckDatFile(fn:str)->bool: - """ Check if the DEVSimPyModel.dat has a goog model and python filename. - """ - - file = Zip.GetDatFile(fn) - - if file != "": - with open(file, 'rb') as sf: - block = pickle.load(sf) - - return block[0] == fn and \ - block[1] == os.path.join(fn,os.path.basename(fn).replace('.amd','.py').replace('.cmd','.py')) - else: - return False + def Check(fn:str)->bool: + """ + """ @staticmethod def GetDatFile(fn:str)->str: @@ -390,15 +361,16 @@ def GetModule(self, rcp: bool=False)->types.ModuleType: #if rcp: recompile(module_name) PluginManager.trigger_event("IMPORT_STRATEGIES", fn=self.fn) - + py_fn = getPythonModelFileName(self.fn) + try: fullname = "".join([os.path.basename(os.path.dirname(self.fn)), py_fn.split('.py')[0]]) return self.ImportModule() if fullname not in sys.modules else sys.modules[fullname] ### model has not python file ! except Exception as e: return e - + def ImportModule(self)->types.ModuleType: """ Import module from zip file corresponding to the amd or cmd model. """ From f07070c7510cd1bfdfba3c584ec6e2e370e5e119 Mon Sep 17 00:00:00 2001 From: Capocchi L Date: Sun, 8 Nov 2020 14:47:13 +0100 Subject: [PATCH 3/3] Add rename method for model and lib. Refactoring of the LibraryTree Class --- Components.py | 24 ++-- LibraryTree.py | 332 ++++++++++++++++++++----------------------------- Menu.py | 7 +- ZipManager.py | 39 +++--- 4 files changed, 170 insertions(+), 232 deletions(-) diff --git a/Components.py b/Components.py index b092d3bc..4bd0cf9f 100644 --- a/Components.py +++ b/Components.py @@ -296,7 +296,6 @@ def ChekFilename(filename, model): def Rename(filename:str, new_name:str)->bool: """ Rename the filename with the new_name. """ - old_bn = os.path.basename(filename) dn = os.path.dirname(filename) @@ -336,28 +335,31 @@ def Rename(filename:str, new_name:str)->bool: if temp_file: - print("Replace %s by %s into %s"%(old_name, new_name, new_temp_file)) + #print("Replace %s by %s into %s"%(old_name, new_name, new_temp_file)) ### replace in new_temp_file file replaceAll(new_temp_file, old_name, new_name) zip = ZipManager.Zip(filename) if zip.Delete([os.path.basename(temp_file)]): - print("Delete %s"%os.path.basename(temp_file)) + #print("Delete %s"%os.path.basename(temp_file)) - print("Update %s"%new_temp_file) - zip.Update([new_temp_file]) + #print("Update %s"%new_temp_file) + zip.Update([new_temp_file]) - print("rename %s to %s"%(filename,new_filepath)) + #print("rename %s to %s"%(filename,new_filepath)) - ### relace on file system - os.rename(filename, new_filepath) + ### relace on file system + os.rename(filename, new_filepath) - return True + return True + else: + return False else: - info = _("It seams that the python filename and the model name are diffrent!\n \ - Please correct this aspect by extracting the archive.") + info = _("Please check this: \n \ + The Python filename and the name of archive (%s)) must be egal to the class name!\n \ + Please correct this aspect by extracting the archive.\n")%(old_name) wx.MessageBox(info, _("Error"), wx.OK|wx.ICON_ERROR) return False diff --git a/LibraryTree.py b/LibraryTree.py index a9262a97..a18416ff 100644 --- a/LibraryTree.py +++ b/LibraryTree.py @@ -155,6 +155,7 @@ def Populate(self, chargedDomainList = []): #threading.Thread(target=self.InsertNewDomain, #args=(absdName, self.root, list(self.GetSubDomain(absdName, self.GetDomainList(absdName)).values())[0],) #).start() + self.InsertNewDomain(absdName, self.root, list(self.GetSubDomain(absdName, self.GetDomainList(absdName)).values())[0]) wx.CallAfter(self.SortChildren,self.root) @@ -437,18 +438,19 @@ def GetPYFileList(dName, ext=".py"): ### import are here because the simulator (PyDEVS or PyPDEVS) require it from DomainInterface.DomainBehavior import DomainBehavior - + try: name_list = getPYFileListFromInit(os.path.join(dName,'__init__.py'), ext) py_file_list = [] - - for s in name_list: + + for s in list(name_list): python_file = os.path.join(dName, s+ext) + ### test if tmp is only composed by python file (case of the user write into the __init__.py file directory name is possible ! then we delete the directory names) if os.path.isfile(python_file): cls = GetClass(python_file) - + if cls is not None and not isinstance(cls, tuple): ### only model that herite from DomainBehavior is shown in lib @@ -516,6 +518,75 @@ def GetModelList(self, dName): return py_file_list + devsimpy_file_list + def AddComponent(self, item, parentPath, parent, p=None): + """ Return id and error of item when added. + """ + + come_from_net = parentPath.startswith('http') + + if item.lower().endswith(('.amd','.cmd')): + ### gestion de l'importation de module (.py) associé au .cmd si le fichier .py n'a jamais été decompresssé (pour edition par exemple) + if not come_from_net: + path = os.path.join(parentPath, item) + zf = Zip(path) + module = zf.GetModule() + image_file = zf.GetImage() + else: + path = "".join([parentPath,'/',item,'.py']) + module = load_module_from_net(path) + + ### check error + error = isinstance(module, Exception) or not Zip.GetBehavioralPythonFile(path) + + ### defalut mcc is null + mcc = 0.0 + + ### change icon depending on the error and the presence of image in amd + if error: + img = self.not_importedidx + elif image_file is not None: + img = self.il.Add(image_file.ConvertToBitmap()) + else: + if item.lower().endswith('.cmd'): + img = self.coupledidx + else: + img = self.atomicidx + ### mcc compuation only for atomic model + mcc = GetMacCabeMetric(path) + + ### insert into the tree + id = self.InsertItemBefore(p if p else parent, 0, os.path.splitext(item)[0], img, img) + + else: + path = os.path.join(parentPath, "".join([item,'.py'])) if not come_from_net else "".join([parentPath,'/',item,'.py']) + + ### try for .pyc + ispyc = False + if not os.path.exists(path): + path = os.path.join(parentPath, "".join([item,'.pyc'])) if not come_from_net else "".join([parentPath,'/',item,'.pyc']) + ispyc = True + + ### Chedk error for DEVS instance + devs = Container.CheckClass(path) + error = isinstance(devs, tuple) + + ### define the propriate img depending on error + img = self.not_importedidx if error else self.pythoncfileidx if ispyc else self.pythonfileidx + + ### insert in the tree + id = self.InsertItemBefore(parent, 0, item, img, img) + + #mcc = float(subprocess.check_output('python {} {}'.format('Complexity.py', path), shell = True)) + mcc = GetMacCabeMetric(path) + + self.SetPyData(id, path) + + self.MetricDico.update({id:{'mcc':mcc, 'parent':parent}}) + s = sum([d['mcc'] for id,d in self.MetricDico.items() if d['parent']==parent]) + self.MetricDico.update({parent:{'mcc':s, 'parent':None}}) + + return (id, error) + ### def InsertNewDomain(self, dName, parent, L = []): """ Recurrent function that insert new Domain on library panel. @@ -523,7 +594,6 @@ def InsertNewDomain(self, dName, parent, L = []): ### first only for the root if dName not in list(self.ItemDico.keys()): - label = os.path.basename(dName) if not dName.startswith('http') else [a for a in dName.split('/') if a!=''][-1] id = self.InsertItemBefore(parent, 0, label) self.SetItemImage(id, self.fldridx, wx.TreeItemIcon_Normal) @@ -538,114 +608,26 @@ def InsertNewDomain(self, dName, parent, L = []): else: item = L.pop(0) - isunicode = isinstance(item, str) isstr = isinstance(item, str) isdict = isinstance(item, dict) ### element to insert in the list D = [] + ### if child is build from DEVSimPy if isstr: - ### parent is retrieved from dict parent = self.ItemDico[dName] assert parent != None ### parent path parentPath = self.GetPyData(parent) - come_from_net = parentPath.startswith('http') ### comma replace item = item.strip() - ### suppression de l'extention su .cmd (model atomic lu à partir de __init__ donc pas d'extention) - if item.endswith('.cmd'): - ### gestion de l'importation de module (.py) associé au .cmd si le fichier .py n'a jamais été decompresssé (pour edition par exemple) - if not come_from_net: - path = os.path.join(parentPath, item) - zf = Zip(path) - module = zf.GetModule() - image_file = zf.GetImage() - else: - path = "".join([parentPath,'/',item,'.py']) - module = load_module_from_net(path) - - ### check error - error = isinstance(module, Exception) - - ### change icon depending on the error and the presence of image in amd - if error: - img = self.not_importedidx - elif image_file is not None: - img = self.il.Add(image_file.ConvertToBitmap()) - else: - img = self.coupledidx - - ### insert into the tree - id = self.InsertItemBefore(parent, 0, os.path.splitext(item)[0], img, img) - self.SetPyData(id, path) - - self.MetricDico.update({id:{'mcc':0.0, 'parent':parent}}) - s = sum([d['mcc'] for id,d in self.MetricDico.items() if d['parent']==parent]) - self.MetricDico.update({parent:{'mcc':s, 'parent':None}}) - - elif item.endswith('.amd'): - ### gestion de l'importation de module (.py) associé au .amd si le fichier .py n'a jamais été decompresssé (pour edition par exemple) - if not come_from_net: - path = os.path.join(parentPath, item) - zf = Zip(path) - module = zf.GetModule() - image_file = zf.GetImage() - else: - path = "".join([parentPath,'/',item,'.py']) - module = load_module_from_net(path) - - ### check error - error = isinstance(module, Exception) - - ### change icon depending on the error and the presence of image in amd - if error: - img = self.not_importedidx - elif image_file is not None: - img = self.il.Add(image_file.ConvertToBitmap()) - else: - img = self.atomicidx - - mcc = GetMacCabeMetric(path) - - ### insert in the tree - id = self.InsertItemBefore(parent, 0, os.path.splitext(item)[0], img, img) - self.SetPyData(id, path) - - self.MetricDico.update({id:{'mcc':mcc, 'parent':parent}}) - s = sum([d['mcc'] for id,d in self.MetricDico.items() if d['parent']==parent]) - self.MetricDico.update({parent:{'mcc':s, 'parent':None}}) - - else: - - path = os.path.join(parentPath, "".join([item,'.py'])) if not come_from_net else "".join([parentPath,'/',item,'.py']) - - ### try for .pyc - ispyc = False - if not os.path.exists(path): - path = os.path.join(parentPath, "".join([item,'.pyc'])) if not come_from_net else "".join([parentPath,'/',item,'.pyc']) - ispyc = True - - devs = Container.CheckClass(path) - - #mcc = float(subprocess.check_output('python {} {}'.format('Complexity.py', path), shell = True)) - mcc = GetMacCabeMetric(path) - - error = isinstance(devs, tuple) - img = self.not_importedidx if error else self.pythoncfileidx if ispyc else self.pythonfileidx - - ### insert in the tree - id = self.InsertItemBefore(parent, 0, item, img, img) - self.SetPyData(id, path) - - self.MetricDico.update({id:{'mcc':mcc, 'parent':parent}}) - s = sum([d['mcc'] for id,d in self.MetricDico.items() if d['parent']==parent]) - self.MetricDico.update({parent:{'mcc':s, 'parent':None}}) + ### only for atomic or coupled model (atomic model is readed from __init__, so no extention) + id, error = self.AddComponent(item, parentPath, parent) ### error info back propagation if error: @@ -655,16 +637,18 @@ def InsertNewDomain(self, dName, parent, L = []): parent = self.GetItemParent(parent) ### insertion des donnees dans l'item et gestion du ItemDico - self.ItemDico.update({os.path.join(parentPath,item,):id}) + self.ItemDico.update({os.path.join(parentPath,item):id}) ### si le fils est un sous repertoire contenant au moins un fichier (all dans __init__.py different de []) elif isdict and list(item.values()) != [[]]: + parentPath = list(item.keys())[0] + ### name to insert in the tree - dName = os.path.basename(list(item.keys())[0]) + dName = os.path.basename(parentPath) ### new parent - parent = self.ItemDico[os.path.dirname(list(item.keys())[0])] if not dName.startswith('http') else self.ItemDico[list(item.keys())[0].replace('/'+dName,'')] + parent = self.ItemDico[os.path.dirname(parentPath)] if not dName.startswith('http') else self.ItemDico[parentPath.replace('/'+dName,'')] assert(parent!=None) @@ -672,114 +656,28 @@ def InsertNewDomain(self, dName, parent, L = []): id = self.InsertItemBefore(parent, 0, dName) self.SetItemBold(id) - self.SetItemImage(id, self.fldridx, wx.TreeItemIcon_Normal) self.SetItemImage(id, self.fldropenidx, wx.TreeItemIcon_Expanded) ### stockage du parent avec pour cle le chemin complet avec extention (pour l'import du moule dans le Dnd) - self.ItemDico.update({list(item.keys())[0]:id}) - self.SetPyData(id,list(item.keys())[0]) + self.ItemDico.update({parentPath:id}) + self.SetPyData(id,parentPath) self.MetricDico.update({id:{'mcc':0.0, 'parent':parent}}) self.MetricDico.update({parent:{'mcc':0.0, 'parent':None}}) ### for the childrens of the sub-domain for elem in list(item.values())[0]: - # si elem simple (modèle couple ou atomic) + # if simple element (coupled or atomic model) if isinstance(elem, str): ### replace the spaces elem = elem.strip() #replace(' ','') - ### parent provisoir - p = self.ItemDico[list(item.keys())[0]] + ### transiant parent + p = self.ItemDico[parentPath] assert(p!=None) - come_from_net = list(item.keys())[0].startswith('http') - ### si model atomic - if elem.endswith('.cmd'): - ### gestion de l'importation de module (.py) associé au .amd si le fichier .py n'a jamais été decompresssé (pour edition par exemple) - if not come_from_net: - path = os.path.join(list(item.keys())[0], elem) - zf = Zip(path) - module = zf.GetModule() - image_file = zf.GetImage() - else: - path = "".join([list(item.keys())[0],'/',elem,'.py']) - module = load_module_from_net(path) - - ### check error - error = isinstance(module, Exception) - - ### change icon depending on the error and the presence of image in amd - if error: - img = self.not_importedidx - elif image_file is not None: - img = self.il.Add(image_file.ConvertToBitmap()) - else: - img = self.coupledidx - - ### insertion dans le tree - id = self.InsertItemBefore(p, 0, os.path.splitext(elem)[0], img, img) - self.SetPyData(id, path) - - self.MetricDico.update({id:{'mcc':0.0, 'parent':parent}}) - s = sum([d['mcc'] for id,d in self.MetricDico.items() if d['parent']==parent]) - self.MetricDico.update({parent:{'mcc':s, 'parent':None}}) - - elif elem.endswith('.amd'): - ### gestion de l'importation de module (.py) associé au .amd si le fichier .py n'a jamais été decompresssé (pour edition par exemple) - if not come_from_net: - path = os.path.join(list(item.keys())[0], elem) - zf = Zip(path) - module = zf.GetModule() - image_file = zf.GetImage() - else: - path = "".join([list(item.keys())[0],'/',elem,'.py']) - module = load_module_from_net(path) - - ### check error - error = isinstance(module, Exception) - - ### change icon depending on the error and the presence of image in amd - if error: - img = self.not_importedidx - elif image_file is not None: - img = self.il.Add(image_file.ConvertToBitmap()) - else: - img = self.atomicidx - - mcc = GetMacCabeMetric(path) - - ### insert in the tree - id = self.InsertItemBefore(p, 0, os.path.splitext(elem)[0], img, img) - self.SetPyData(id, path) - - self.MetricDico.update({id:{'mcc':mcc, 'parent':parent}}) - s = sum([d['mcc'] for id,d in self.MetricDico.items() if d['parent']==parent]) - self.MetricDico.update({parent:{'mcc':s, 'parent':None}}) - - else: - - path = os.path.join(list(item.keys())[0],"".join([elem,'.py'])) if not list(item.keys())[0].startswith('http') else list(item.keys())[0]+'/'+elem+'.py' - ### try for .pyc file - ispyc = False - if not os.path.exists(path): - path = os.path.join(list(item.keys())[0],"".join([elem,'.pyc'])) if not list(item.keys())[0].startswith('http') else list(item.keys())[0]+'/'+elem+'.pyc' - ispyc = True - - devs = Container.CheckClass(path) - - mcc = GetMacCabeMetric(path) - - error = isinstance(devs, tuple) - img = self.not_importedidx if error else self.pythoncfileidx if ispyc else self.pythonfileidx - - ### insert in the tree - id = self.InsertItemBefore(p, 0, elem, img, img) - self.SetPyData(id, path) - self.MetricDico.update({id:{'mcc':mcc, 'parent':parent}}) - - s = sum([d['mcc'] for id,d in self.MetricDico.items() if d['parent']==parent]) - self.MetricDico.update({parent:{'mcc':s, 'parent':None}}) + + id, error = self.AddComponent(elem, parentPath, parent, p) ### error info back propagation if error: @@ -789,24 +687,21 @@ def InsertNewDomain(self, dName, parent, L = []): ### next parent item p = self.GetItemParent(p) - self.ItemDico.update({os.path.join(list(item.keys())[0], elem):id}) + self.ItemDico.update({os.path.join(parentPath, elem):id}) else: ### in order to go up the information in the list D.append(elem) ### update with whole name - dName = list(item.keys())[0] + dName = parentPath ### for spash screen try: ### format the string depending the nature of the item - if isdict: - item = " ".join([os.path.basename(list(item.keys())[0]), 'from', os.path.basename(os.path.dirname(list(item.keys())[0]))]) - else: - item = " ".join([item, 'from', os.path.basename(dName)]) - - pub.sendMessage('object.added', message='Loading %s domain...'%item) + info = " ".join([os.path.basename(parentPath), 'from', os.path.basename(os.path.dirname(parentPath))]) if isdict \ + else " ".join([item, 'from', os.path.basename(dName)]) + pub.sendMessage('object.added', message='Loading %s domain...'%info) except: pass @@ -1096,6 +991,43 @@ def OnItemEdit(self, evt): ### call frame editor DEVSComponent.OnEditor(devscomp, evt) + ### + def OnDirRename(self, evt): + """ Rename the directory of selected librarie. + """ + item = self.GetSelection() + name = self.GetItemText(item) + + ### dialog to ask new label + if wx.VERSION_STRING < '4.0': + d = wx.TextEntryDialog(self, _('New file name:'), defaultValue = name, style=wx.OK) + else: + d = wx.TextEntryDialog(self, _('New file name:'), value = name, style=wx.OK) + d.ShowModal() + + ### new label + new_label = d.GetValue() + + ### only if new and old label are different + if new_label != name: + + ### path of file + old_dirname = self.GetItemPyData(item) + new_dirname = os.path.join(os.path.dirname(old_dirname),new_label) + + try: + os.rename(old_dirname,new_dirname) + except: + sys.stdout.write(_('Rename failed!')) + else: + self.SetItemData(item,new_dirname) + for elem in self.ItemDico: + if old_dirname in elem: + self.ItemDico[elem.replace(old_dirname, new_dirname)] = self.ItemDico.pop(elem) + + self.SetItemText(item, new_label) + self.UpdateAll() + ### def OnItemRename(self, evt): """ Rename action has been invoked. diff --git a/Menu.py b/Menu.py index 702bd3a1..57c7b36a 100644 --- a/Menu.py +++ b/Menu.py @@ -148,6 +148,7 @@ ID_IMPORT_LIB = wx.NewIdRef() ID_EDIT_LIB = wx.NewIdRef() ID_RENAME_LIB = wx.NewIdRef() +ID_RENAME_DIR_LIB = wx.NewIdRef() ID_REFRESH_LIB = wx.NewIdRef() ID_MCC_LIB = wx.NewIdRef() ID_UPGRADE_LIB = wx.NewIdRef() @@ -729,10 +730,14 @@ def __init__(self, parent): new_dir = wx.MenuItem(self, ID_NEW_DIR_LIB, _('New sub directory'), _('Add a directory to the selected library')) new_dir.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH_16_16, 'new.png'))) InsertItem(1, new_dir) + rename_dir = wx.MenuItem(self, ID_RENAME_DIR_LIB, _('Rename'), _('Rename selected librarie')) + rename_dir.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH_16_16,'rename.png'))) + InsertItem(2, rename_dir) update_lib = wx.MenuItem(self, ID_UPDATE_SUBLIB, _('Update'), _('Update all models of the selected library')) update_lib.SetBitmap(wx.Bitmap(os.path.join(ICON_PATH_16_16, 'db_refresh2.png'))) - InsertItem(2, update_lib) + InsertItem(3, update_lib) self.Bind(wx.EVT_MENU, parent.OnNewModel, id=ID_NEW_MODEL_LIB) + self.Bind(wx.EVT_MENU, parent.OnDirRename, id=ID_RENAME_DIR_LIB) self.Bind(wx.EVT_MENU, parent.OnNewDir, id=ID_NEW_DIR_LIB) self.Bind(wx.EVT_MENU, parent.OnUpdateSubLib, id=ID_UPDATE_SUBLIB) diff --git a/ZipManager.py b/ZipManager.py index ac824712..d90c5faf 100644 --- a/ZipManager.py +++ b/ZipManager.py @@ -47,7 +47,7 @@ def getPythonModelFileName(fn:str)->str: #global Cmtp - assert(zipfile.is_zipfile(fn)) + assert zipfile.is_zipfile(fn), fn zf = zipfile.ZipFile(fn,'r') @@ -219,11 +219,6 @@ def Delete(self, delete_files:[str]=[])->None: return del_flag - @staticmethod - def Check(fn:str)->bool: - """ - """ - @staticmethod def GetDatFile(fn:str)->str: """ Return the DEVSimPyModel.dat file. @@ -268,28 +263,32 @@ def GetImage(self, scaleW:int=16, scaleH:int=16): return None @staticmethod - def GetBehavioralPythonFile(fn:str)->str: + def GetBehavioralPythonFile(fn:str): """ TODO: comment """ - if not zipfile.is_zipfile(fn): - return "" - ### zipfile (amd or cmd) - zf = zipfile.ZipFile(fn, 'r') - nl = zf.namelist() - zf.close() + if not zipfile.is_zipfile(fn): + return False bn = os.path.basename(fn) name, ext = os.path.splitext(bn) - for s in [s for s in nl if s.endswith(".py")]: - n, e = os.path.splitext(s) - r = repr(zf.read(s)) - if n == name and 'DomainBehavior' in r or 'DomainStructure' in r: - return s - - return "" + with zipfile.ZipFile(fn) as zf: + ### find all python files + for file in zf.namelist(): + if file.endswith(".py"): + r = repr(zf.read(file)) + n, e = os.path.splitext(file) + if n == name and ('class %s(DomainBehavior)'%name in r or 'class %s(DomainStructure)'%name in r): + return file + + info = _("Please check this: \n \ + The Python filename and the name of archive (%s)) must be egal to the class name!\n \ + Please correct this aspect by extracting the archive.\n")%(name) + sys.stdout.write(info) + + return False @staticmethod def GetPluginFile(fn:str)->str: