Salome HOME
Update version
[tools/eficas.git] / Noyau / N_ETAPE.py
1 # coding=utf-8
2 # Copyright (C) 2007-2021   EDF R&D
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19
20
21 """
22     Ce module contient la classe ETAPE qui sert a verifier et a executer
23     une commande
24 """
25
26 # Modules Python
27 from __future__ import absolute_import
28 from __future__ import print_function
29 try :
30     from builtins import str
31 except :
32     pass
33 import types
34 import sys
35 import os
36 import linecache
37 import traceback
38 from copy import copy
39
40 # Modules EFICAS
41 from . import N_MCCOMPO
42 from .N_Exception import AsException
43 from . import N_utils
44 from .N_utils import AsType
45 from .N_ASSD import ASSD
46
47
48 class ETAPE(N_MCCOMPO.MCCOMPO):
49
50     """
51        Cette classe herite de MCCOMPO car ETAPE est un OBJECT composite
52
53     """
54     nature = "OPERATEUR"
55
56     # L'attribut de classe codex est utilise pour rattacher le module de calcul eventuel (voir Build)
57     # On le met a None pour indiquer qu'il n'y a pas de module de calcul
58     # rattache
59     codex = None
60
61     def __init__(self, oper=None, reuse=None, args={}, niveau=4):
62         """
63         Attributs :
64          - definition : objet portant les attributs de definition d'une etape de type operateur. Il
65                         est initialise par l'argument oper.
66          - reuse : indique le concept d'entree reutilise. Il se trouvera donc en sortie
67                    si les conditions d'execution de l'operateur l'autorise
68          - valeur : arguments d'entree de type mot-cle=valeur. Initialise avec l'argument args.
69          - objPyxbDeConstruction
70         """
71         # faut il le faire ds MC_Build ?
72         # traitement de Pyxb si Pyxb
73         self.dicoPyxbDeConstruction = args.get('dicoPyxbDeConstruction', None)
74         if self.dicoPyxbDeConstruction :
75             del args['dicoPyxbDeConstruction']
76             self.objPyxbDeConstruction=self.dicoPyxbDeConstruction['objEnPyxb']
77         else :
78             self.objPyxbDeConstruction=None
79         self.definition = oper
80         self.reuse = reuse
81         self.valeur = args
82         self.nettoiargs()
83         self.parent = CONTEXT.getCurrentStep()
84         self.etape = self
85         self.nom = oper.nom
86         self.idracine = oper.label
87         self.appel = N_utils.calleeWhere(niveau)
88         self.mc_globaux = {}
89         self.doitEtreRecalculee = False
90         self.sd = None
91         self.actif = 1
92         self.makeRegister()
93         self.icmd = None
94         self.userASSDCrees=[]
95
96     def makeRegister(self):
97         """
98         Initialise les attributs jdc, id, niveau et realise les
99         enregistrements necessaires
100         surcharge dans Ihm
101         """
102         #print ('makeRegister de  ETAPE')
103         if self.parent:
104             self.jdc = self.parent.getJdcRoot()
105             self.id = self.parent.register(self)
106             self.niveau = None
107         else:
108             self.jdc = self.parent = None
109             self.id = None
110             self.niveau = None
111
112     def nettoiargs(self):
113         """
114            Cette methode a pour fonction de retirer tous les arguments egaux a None
115            de la liste des arguments. Ils sont supposes non presents et donc retires.
116         """
117         for k in list(self.valeur.keys()):
118             if self.valeur[k] == None:
119                 del self.valeur[k]
120
121     def MCBuild(self):
122         """
123            Demande la construction des sous-objets et les stocke dans l'attribut
124            mcListe.
125         """
126         self.mcListe = self.buildMc()
127
128     def buildSd(self, nom):
129         """
130            Construit le concept produit de l'operateur. Deux cas
131            peuvent se presenter :
132
133              - le parent n'est pas defini. Dans ce cas, l'etape prend en charge la creation
134                et le nommage du concept.
135
136              - le parent est defini. Dans ce cas, l'etape demande au parent la creation et
137                le nommage du concept.
138
139         """
140         self.sdnom = nom
141         try:
142             if self.parent:
143                 sd = self.parent.createSdprod(self, nom)
144                 if type(self.definition.op_init) == types.FunctionType:
145                     self.definition.op_init(*(
146                         self, self.parent.g_context))
147             else:
148                 sd = self.getSdProd()
149                 # On n'utilise pas self.definition.op_init car self.parent
150                 # n'existe pas
151                 if sd != None and self.reuse == None:
152                     # On ne nomme le concept que dans le cas de non reutilisation
153                     # d un concept
154                     sd.setName(nom)
155         except AsException as e:
156             raise AsException("Etape ", self.nom, 'ligne : ', self.appel[0],
157                               'fichier : ', self.appel[1], e)
158         except EOFError:
159             raise
160         except:
161             l = traceback.format_exception(
162                 sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])
163             raise AsException("Etape ", self.nom, 'ligne : ', self.appel[0],
164                               'fichier : ', self.appel[1] + '\n',
165                               ''.join(l))
166
167         self.Execute()
168         return sd
169
170     def Execute(self):
171         """
172            Cette methode est un point d'entree prevu pour realiser une execution immediatement
173            apres avoir construit les mots cles et le concept produit.
174            Par defaut, elle ne fait rien. Elle doit etre surchargee dans une autre partie du programme.
175         """
176         return
177
178     def getSdProd(self):
179         """
180             Retourne le concept resultat de l'etape
181             Deux cas :
182                      - cas 1 : sd_prod de oper n'est pas une fonction
183                        il s'agit d'une sous classe de ASSD
184                        on construit le sd a partir de cette classe
185                        et on le retourne
186                      - cas 2 : il s'agit d'une fonction
187                        on l'evalue avec les mots-cles de l'etape (mcListe)
188                        on construit le sd a partir de la classe obtenue
189                        et on le retourne
190         """
191         if type(self.definition.sd_prod) == types.FunctionType:
192             d = self.creeDictValeurs(self.mcListe)
193             try:
194                 sd_prod = self.definition.sd_prod(*(), **d)
195             except EOFError:
196                 raise
197             except Exception as exc:
198                 if CONTEXT.debug:
199                     traceback.print_exc()
200                 raise AsException("impossible d affecter un type au resultat:",
201                                   str(exc))
202         else:
203             sd_prod = self.definition.sd_prod
204         # on teste maintenant si la SD est reutilisee ou s'il faut la creer
205         if self.definition.reentrant != 'n' and self.reuse:
206             # Le concept produit est specifie reutilise (reuse=xxx). C'est une erreur mais non fatale.
207             # Elle sera traitee ulterieurement.
208             self.sd = self.reuse
209         else:
210             self.sd = sd_prod(etape=self)
211             # Si l'operateur est obligatoirement reentrant et reuse n'a pas ete specifie, c'est une erreur.
212             # On ne fait rien ici. L'erreur sera traiter par la suite.
213         # precaution
214         if self.sd is not None and not isinstance(self.sd, ASSD):
215             raise AsException("""
216 Impossible de typer le resultat !
217 Causes possibles :
218    Utilisateur : Soit la valeur fournie derrière "reuse" est incorrecte,
219                  soit il y a une "," a la fin d'une commande precedente.
220    Developpeur : La fonction "sd_prod" retourne un type invalide.""")
221         return self.sd
222
223     def getType_produit(self):
224         try:
225             return self.getType_produit_brut()
226         except:
227             return None
228
229     def getType_produit_brut(self):
230         """
231             Retourne le type du concept resultat de l'etape
232             Deux cas :
233               - cas 1 : sd_prod de oper n'est pas une fonction
234                 il s'agit d'une sous classe de ASSD
235                 on retourne le nom de la classe
236               - cas 2 : il s'agit d'une fonction
237                 on l'evalue avec les mots-cles de l'etape (mcListe)
238                 et on retourne son resultat
239         """
240         if type(self.definition.sd_prod) == types.FunctionType:
241             d = self.creeDictValeurs(self.mcListe)
242             sd_prod = self.definition.sd_prod(*(), **d)
243         else:
244             sd_prod = self.definition.sd_prod
245         return sd_prod
246
247     def getEtape(self):
248         """
249            Retourne l'etape a laquelle appartient self
250            Un objet de la categorie etape doit retourner self pour indiquer que
251            l'etape a ete trouvee
252         """
253         return self
254
255     def supprime(self):
256         """
257            Methode qui supprime toutes les references arrières afin que l'objet puisse
258            etre correctement detruit par le garbage collector
259         """
260         N_MCCOMPO.MCCOMPO.supprime(self)
261         self.jdc = None
262         self.appel = None
263         for name in dir(self):
264             if name.startswith('_cache_'):
265                 setattr(self, name, None)
266         if self.sd:
267             self.sd.supprime()
268
269     def __del__(self):
270         pass
271
272     def getCreated_sd(self):
273         """Retourne la liste des sd reellement produites par l'etape.
274         Si reuse est present, `self.sd` a ete creee avant, donc n'est pas dans
275         cette liste."""
276         if not self.reuse and self.sd:
277             return [self.sd, ]
278         return []
279
280     def isActif(self):
281         """
282            Indique si l'etape est active (1) ou inactive (0)
283         """
284         return self.actif
285
286     def setCurrentStep(self):
287         """
288             Methode utilisee pour que l etape self se declare etape
289             courante. Utilise par les macros
290         """
291         cs = CONTEXT.getCurrentStep()
292         if self.parent != cs:
293             raise AsException("L'etape courante", cs.nom, cs,
294                               "devrait etre le parent de", self.nom, self)
295         else:
296             CONTEXT.unsetCurrentStep()
297             CONTEXT.setCurrentStep(self)
298
299     def resetCurrentStep(self):
300         """
301               Methode utilisee par l'etape self qui remet son etape parent comme
302               etape courante
303         """
304         cs = CONTEXT.getCurrentStep()
305         if self != cs:
306             raise AsException("L'etape courante", cs.nom, cs,
307                               "devrait etre", self.nom, self)
308         else:
309             CONTEXT.unsetCurrentStep()
310             CONTEXT.setCurrentStep(self.parent)
311
312     def issubstep(self, etape):
313         """
314             Cette methode retourne un entier indiquant si etape est une
315             sous etape de self ou non
316             1 = oui
317             0 = non
318             Une etape simple n'a pas de sous etape
319         """
320         return 0
321
322     def getFile(self, unite=None, fic_origine='', fname=None):
323         """
324             Retourne le nom du fichier correspondant a un numero d'unite
325             logique (entier) ainsi que le source contenu dans le fichier
326         """
327         if self.jdc:
328             return self.jdc.getFile(unite=unite, fic_origine=fic_origine, fname=fname)
329         else:
330             if unite != None:
331                 if os.path.exists("fort." + str(unite)):
332                     fname = "fort." + str(unite)
333             if fname == None:
334                 raise AsException("Impossible de trouver le fichier correspondant"
335                                   " a l unite %s" % unite)
336             if not os.path.exists(fname):
337                 raise AsException("%s n'est pas un fichier existant" % unite)
338             fproc = open(fname, 'r')
339             text = fproc.read()
340             fproc.close()
341             text = text.replace('\r\n', '\n')
342             linecache.cache[fname] = 0, 0, text.split('\n'), fname
343             return fname, text
344
345     def accept(self, visitor):
346         """
347            Cette methode permet de parcourir l'arborescence des objets
348            en utilisant le pattern VISITEUR
349         """
350         visitor.visitETAPE(self)
351
352     def updateContext(self, d):
353         """
354             Cette methode doit updater le contexte fournit par
355             l'appelant en argument (d) en fonction de sa definition
356         """
357         if type(self.definition.op_init) == types.FunctionType:
358             self.definition.op_init(*(self, d))
359         if self.sd:
360             d[self.sd.nom] = self.sd
361
362     def copy(self):
363         """ Methode qui retourne une copie de self non enregistree auprès du JDC
364             et sans sd
365         """
366         etape = copy(self)
367         etape.sd = None
368         etape.state = 'modified'
369         etape.reuse = None
370         etape.sdnom = None
371         etape.etape = etape
372         etape.mcListe = []
373         for objet in self.mcListe:
374             new_obj = objet.copy()
375             new_obj.reparent(etape)
376             etape.mcListe.append(new_obj)
377         return etape
378
379     def copyReuse(self, old_etape):
380         """ Methode qui copie le reuse d'une autre etape.
381         """
382         if hasattr(old_etape, "reuse"):
383             self.reuse = old_etape.reuse
384
385     def copySdnom(self, old_etape):
386         """ Methode qui copie le sdnom d'une autre etape.
387         """
388         if hasattr(old_etape, "sdnom"):
389             self.sdnom = old_etape.sdnom
390
391     def reparent(self, parent):
392         """
393             Cette methode sert a reinitialiser la parente de l'objet
394         """
395         self.parent = parent
396         self.jdc = parent.getJdcRoot()
397         self.etape = self
398         for mocle in self.mcListe:
399             mocle.reparent(self)
400         if self.sd and self.reuse == None:
401             self.sd.jdc = self.jdc
402
403     def getCmd(self, nomcmd):
404         """
405             Methode pour recuperer la definition d'une commande
406             donnee par son nom dans les catalogues declares
407             au niveau du jdc
408             Appele par un ops d'une macro en Python
409         """
410         return self.jdc.getCmd(nomcmd)
411
412     def copyIntern(self, etape):
413         """
414             Methode permettant lors du processus de recopie de copier
415             les elements internes d'une etape dans une autre
416         """
417         return
418
419     def fullCopy(self, parent=None):
420         """
421            Methode permettant d'effectuer une copie complète
422            d'une etape (y compris concept produit, elements internes)
423            Si l'argument parent est fourni, la nouvelle etape
424            aura cet objet comme parent.
425         """
426         new_etape = self.copy()
427         new_etape.copyReuse(self)
428         new_etape.copySdnom(self)
429         if parent:
430             new_etape.reparent(parent)
431         if self.sd:
432             new_sd = self.sd.__class__(etape=new_etape)
433             new_etape.sd = new_sd
434             if self.reuse == None:
435                 new_etape.parent.nommerSDProd(new_sd, self.sd.nom)
436             else:
437                 new_sd.setName(self.sd.nom)
438         new_etape.copyIntern(self)
439         return new_etape
440
441     def resetJdc(self, new_jdc):
442         """
443            Reinitialise le nommage du concept de l'etape lors d'un changement de jdc
444         """
445         if self.sd and self.reuse == None:
446             self.parent.nommerSDProd(self.sd, self.sd.nom)
447
448     def isInclude(self):
449         """Permet savoir si on a affaire a la commande INCLUDE
450         car le comportement de ces macros est particulier.
451         """
452         return self.nom.startswith('INCLUDE')
453
454     def sdAccessible(self):
455         """Dit si on peut acceder aux "valeurs" (jeveux) de l'ASSD produite par l'etape.
456         """
457         if CONTEXT.debug:
458             print(('`- ETAPE sdAccessible :', self.nom))
459         return self.parent.sdAccessible()
460
461     def getConcept(self, nomsd):
462         """
463             Methode pour recuperer un concept a partir de son nom
464         """
465         # pourrait etre appelee par une commande fortran faisant appel a des fonctions python
466         # on passe la main au parent
467         return self.parent.getConcept(nomsd)