Salome HOME
fin du menage
[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.sd = None
90         self.actif = 1
91         self.makeRegister()
92         self.icmd = None
93
94     def makeRegister(self):
95         """
96         Initialise les attributs jdc, id, niveau et realise les
97         enregistrements necessaires
98         surcharge dans Ihm
99         """
100         #print ('makeRegister de  ETAPE')
101         if self.parent:
102             self.jdc = self.parent.getJdcRoot()
103             self.id = self.parent.register(self)
104             self.niveau = None
105         else:
106             self.jdc = self.parent = None
107             self.id = None
108             self.niveau = None
109
110     def nettoiargs(self):
111         """
112            Cette methode a pour fonction de retirer tous les arguments egaux a None
113            de la liste des arguments. Ils sont supposes non presents et donc retires.
114         """
115         for k in list(self.valeur.keys()):
116             if self.valeur[k] == None:
117                 del self.valeur[k]
118
119     def MCBuild(self):
120         """
121            Demande la construction des sous-objets et les stocke dans l'attribut
122            mcListe.
123         """
124         self.mcListe = self.buildMc()
125
126     def buildSd(self, nom):
127         """
128            Construit le concept produit de l'operateur. Deux cas
129            peuvent se presenter :
130
131              - le parent n'est pas defini. Dans ce cas, l'etape prend en charge la creation
132                et le nommage du concept.
133
134              - le parent est defini. Dans ce cas, l'etape demande au parent la creation et
135                le nommage du concept.
136
137         """
138         self.sdnom = nom
139         try:
140             if self.parent:
141                 sd = self.parent.createSdprod(self, nom)
142                 if type(self.definition.op_init) == types.FunctionType:
143                     self.definition.op_init(*(
144                         self, self.parent.g_context))
145             else:
146                 sd = self.getSdProd()
147                 # On n'utilise pas self.definition.op_init car self.parent
148                 # n'existe pas
149                 if sd != None and self.reuse == None:
150                     # On ne nomme le concept que dans le cas de non reutilisation
151                     # d un concept
152                     sd.setName(nom)
153         except AsException as e:
154             raise AsException("Etape ", self.nom, 'ligne : ', self.appel[0],
155                               'fichier : ', self.appel[1], e)
156         except EOFError:
157             raise
158         except:
159             l = traceback.format_exception(
160                 sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])
161             raise AsException("Etape ", self.nom, 'ligne : ', self.appel[0],
162                               'fichier : ', self.appel[1] + '\n',
163                               ''.join(l))
164
165         self.Execute()
166         return sd
167
168     def Execute(self):
169         """
170            Cette methode est un point d'entree prevu pour realiser une execution immediatement
171            apres avoir construit les mots cles et le concept produit.
172            Par defaut, elle ne fait rien. Elle doit etre surchargee dans une autre partie du programme.
173         """
174         return
175
176     def getSdProd(self):
177         """
178             Retourne le concept resultat de l'etape
179             Deux cas :
180                      - cas 1 : sd_prod de oper n'est pas une fonction
181                        il s'agit d'une sous classe de ASSD
182                        on construit le sd a partir de cette classe
183                        et on le retourne
184                      - cas 2 : il s'agit d'une fonction
185                        on l'evalue avec les mots-cles de l'etape (mcListe)
186                        on construit le sd a partir de la classe obtenue
187                        et on le retourne
188         """
189         if type(self.definition.sd_prod) == types.FunctionType:
190             d = self.creeDictValeurs(self.mcListe)
191             try:
192                 sd_prod = self.definition.sd_prod(*(), **d)
193             except EOFError:
194                 raise
195             except Exception as exc:
196                 if CONTEXT.debug:
197                     traceback.print_exc()
198                 raise AsException("impossible d affecter un type au resultat:",
199                                   str(exc))
200         else:
201             sd_prod = self.definition.sd_prod
202         # on teste maintenant si la SD est reutilisee ou s'il faut la creer
203         if self.definition.reentrant != 'n' and self.reuse:
204             # Le concept produit est specifie reutilise (reuse=xxx). C'est une erreur mais non fatale.
205             # Elle sera traitee ulterieurement.
206             self.sd = self.reuse
207         else:
208             self.sd = sd_prod(etape=self)
209             # Si l'operateur est obligatoirement reentrant et reuse n'a pas ete specifie, c'est une erreur.
210             # On ne fait rien ici. L'erreur sera traiter par la suite.
211         # precaution
212         if self.sd is not None and not isinstance(self.sd, ASSD):
213             raise AsException("""
214 Impossible de typer le resultat !
215 Causes possibles :
216    Utilisateur : Soit la valeur fournie derrière "reuse" est incorrecte,
217                  soit il y a une "," a la fin d'une commande precedente.
218    Developpeur : La fonction "sd_prod" retourne un type invalide.""")
219         return self.sd
220
221     def getType_produit(self):
222         try:
223             return self.getType_produit_brut()
224         except:
225             return None
226
227     def getType_produit_brut(self):
228         """
229             Retourne le type du concept resultat de l'etape
230             Deux cas :
231               - cas 1 : sd_prod de oper n'est pas une fonction
232                 il s'agit d'une sous classe de ASSD
233                 on retourne le nom de la classe
234               - cas 2 : il s'agit d'une fonction
235                 on l'evalue avec les mots-cles de l'etape (mcListe)
236                 et on retourne son resultat
237         """
238         if type(self.definition.sd_prod) == types.FunctionType:
239             d = self.creeDictValeurs(self.mcListe)
240             sd_prod = self.definition.sd_prod(*(), **d)
241         else:
242             sd_prod = self.definition.sd_prod
243         return sd_prod
244
245     def getEtape(self):
246         """
247            Retourne l'etape a laquelle appartient self
248            Un objet de la categorie etape doit retourner self pour indiquer que
249            l'etape a ete trouvee
250         """
251         return self
252
253     def supprime(self):
254         """
255            Methode qui supprime toutes les references arrières afin que l'objet puisse
256            etre correctement detruit par le garbage collector
257         """
258         N_MCCOMPO.MCCOMPO.supprime(self)
259         self.jdc = None
260         self.appel = None
261         for name in dir(self):
262             if name.startswith('_cache_'):
263                 setattr(self, name, None)
264         if self.sd:
265             self.sd.supprime()
266
267     def __del__(self):
268         pass
269
270     def getCreated_sd(self):
271         """Retourne la liste des sd reellement produites par l'etape.
272         Si reuse est present, `self.sd` a ete creee avant, donc n'est pas dans
273         cette liste."""
274         if not self.reuse and self.sd:
275             return [self.sd, ]
276         return []
277
278     def isActif(self):
279         """
280            Indique si l'etape est active (1) ou inactive (0)
281         """
282         return self.actif
283
284     def setCurrentStep(self):
285         """
286             Methode utilisee pour que l etape self se declare etape
287             courante. Utilise par les macros
288         """
289         cs = CONTEXT.getCurrentStep()
290         if self.parent != cs:
291             raise AsException("L'etape courante", cs.nom, cs,
292                               "devrait etre le parent de", self.nom, self)
293         else:
294             CONTEXT.unsetCurrentStep()
295             CONTEXT.setCurrentStep(self)
296
297     def resetCurrentStep(self):
298         """
299               Methode utilisee par l'etape self qui remet son etape parent comme
300               etape courante
301         """
302         cs = CONTEXT.getCurrentStep()
303         if self != cs:
304             raise AsException("L'etape courante", cs.nom, cs,
305                               "devrait etre", self.nom, self)
306         else:
307             CONTEXT.unsetCurrentStep()
308             CONTEXT.setCurrentStep(self.parent)
309
310     def issubstep(self, etape):
311         """
312             Cette methode retourne un entier indiquant si etape est une
313             sous etape de self ou non
314             1 = oui
315             0 = non
316             Une etape simple n'a pas de sous etape
317         """
318         return 0
319
320     def getFile(self, unite=None, fic_origine='', fname=None):
321         """
322             Retourne le nom du fichier correspondant a un numero d'unite
323             logique (entier) ainsi que le source contenu dans le fichier
324         """
325         if self.jdc:
326             return self.jdc.getFile(unite=unite, fic_origine=fic_origine, fname=fname)
327         else:
328             if unite != None:
329                 if os.path.exists("fort." + str(unite)):
330                     fname = "fort." + str(unite)
331             if fname == None:
332                 raise AsException("Impossible de trouver le fichier correspondant"
333                                   " a l unite %s" % unite)
334             if not os.path.exists(fname):
335                 raise AsException("%s n'est pas un fichier existant" % unite)
336             fproc = open(fname, 'r')
337             text = fproc.read()
338             fproc.close()
339             text = text.replace('\r\n', '\n')
340             linecache.cache[fname] = 0, 0, text.split('\n'), fname
341             return fname, text
342
343     def accept(self, visitor):
344         """
345            Cette methode permet de parcourir l'arborescence des objets
346            en utilisant le pattern VISITEUR
347         """
348         visitor.visitETAPE(self)
349
350     def updateContext(self, d):
351         """
352             Cette methode doit updater le contexte fournit par
353             l'appelant en argument (d) en fonction de sa definition
354         """
355         if type(self.definition.op_init) == types.FunctionType:
356             self.definition.op_init(*(self, d))
357         if self.sd:
358             d[self.sd.nom] = self.sd
359
360     def copy(self):
361         """ Methode qui retourne une copie de self non enregistree auprès du JDC
362             et sans sd
363         """
364         etape = copy(self)
365         etape.sd = None
366         etape.state = 'modified'
367         etape.reuse = None
368         etape.sdnom = None
369         etape.etape = etape
370         etape.mcListe = []
371         for objet in self.mcListe:
372             new_obj = objet.copy()
373             new_obj.reparent(etape)
374             etape.mcListe.append(new_obj)
375         return etape
376
377     def copyReuse(self, old_etape):
378         """ Methode qui copie le reuse d'une autre etape.
379         """
380         if hasattr(old_etape, "reuse"):
381             self.reuse = old_etape.reuse
382
383     def copySdnom(self, old_etape):
384         """ Methode qui copie le sdnom d'une autre etape.
385         """
386         if hasattr(old_etape, "sdnom"):
387             self.sdnom = old_etape.sdnom
388
389     def reparent(self, parent):
390         """
391             Cette methode sert a reinitialiser la parente de l'objet
392         """
393         self.parent = parent
394         self.jdc = parent.getJdcRoot()
395         self.etape = self
396         for mocle in self.mcListe:
397             mocle.reparent(self)
398         if self.sd and self.reuse == None:
399             self.sd.jdc = self.jdc
400
401     def getCmd(self, nomcmd):
402         """
403             Methode pour recuperer la definition d'une commande
404             donnee par son nom dans les catalogues declares
405             au niveau du jdc
406             Appele par un ops d'une macro en Python
407         """
408         return self.jdc.getCmd(nomcmd)
409
410     def copyIntern(self, etape):
411         """
412             Methode permettant lors du processus de recopie de copier
413             les elements internes d'une etape dans une autre
414         """
415         return
416
417     def fullCopy(self, parent=None):
418         """
419            Methode permettant d'effectuer une copie complète
420            d'une etape (y compris concept produit, elements internes)
421            Si l'argument parent est fourni, la nouvelle etape
422            aura cet objet comme parent.
423         """
424         new_etape = self.copy()
425         new_etape.copyReuse(self)
426         new_etape.copySdnom(self)
427         if parent:
428             new_etape.reparent(parent)
429         if self.sd:
430             new_sd = self.sd.__class__(etape=new_etape)
431             new_etape.sd = new_sd
432             if self.reuse == None:
433                 new_etape.parent.nommerSDProd(new_sd, self.sd.nom)
434             else:
435                 new_sd.setName(self.sd.nom)
436         new_etape.copyIntern(self)
437         return new_etape
438
439     def resetJdc(self, new_jdc):
440         """
441            Reinitialise le nommage du concept de l'etape lors d'un changement de jdc
442         """
443         if self.sd and self.reuse == None:
444             self.parent.nommerSDProd(self.sd, self.sd.nom)
445
446     def isInclude(self):
447         """Permet savoir si on a affaire a la commande INCLUDE
448         car le comportement de ces macros est particulier.
449         """
450         return self.nom.startswith('INCLUDE')
451
452     def sdAccessible(self):
453         """Dit si on peut acceder aux "valeurs" (jeveux) de l'ASSD produite par l'etape.
454         """
455         if CONTEXT.debug:
456             print(('`- ETAPE sdAccessible :', self.nom))
457         return self.parent.sdAccessible()
458
459     def getConcept(self, nomsd):
460         """
461             Methode pour recuperer un concept a partir de son nom
462         """
463         # pourrait etre appelee par une commande fortran faisant appel a des fonctions python
464         # on passe la main au parent
465         return self.parent.getConcept(nomsd)
466