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