Salome HOME
mise en cohesion 78
[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 à vérifier et à exécuter
23     une commande
24 """
25
26 # Modules Python
27 import types
28 import sys
29 import string
30 import os
31 import linecache
32 import traceback
33 from copy import copy
34
35 # Modules EFICAS
36 import N_MCCOMPO
37 from N_Exception import AsException
38 import N_utils
39 from N_utils import AsType
40 from N_ASSD import ASSD
41 from N_info import message, SUPERV
42
43
44 class ETAPE(N_MCCOMPO.MCCOMPO):
45
46     """
47        Cette classe hérite de MCCOMPO car ETAPE est un OBJECT composite
48
49     """
50     nature = "OPERATEUR"
51
52     # L'attribut de classe codex est utilisé pour rattacher le module de calcul éventuel (voir Build)
53     # On le met à None pour indiquer qu'il n'y a pas de module de calcul
54     # rattaché
55     codex = None
56
57     def __init__(self, oper=None, reuse=None, args={}, niveau=4):
58         """
59         Attributs :
60          - definition : objet portant les attributs de définition d'une étape de type opérateur. Il
61                         est initialisé par l'argument oper.
62          - reuse : indique le concept d'entrée réutilisé. Il se trouvera donc en sortie
63                    si les conditions d'exécution de l'opérateur l'autorise
64          - valeur : arguments d'entrée de type mot-clé=valeur. Initialisé avec l'argument args.
65         """
66         self.definition = oper
67         self.reuse = reuse
68         self.valeur = args
69         self.nettoiargs()
70         self.parent = CONTEXT.get_current_step()
71         self.etape = self
72         self.nom = oper.nom
73         self.idracine = oper.label
74         self.appel = N_utils.callee_where(niveau)
75         self.mc_globaux = {}
76         self.sd = None
77         self.actif = 1
78         self.make_register()
79         self.icmd = None
80
81     def make_register(self):
82         """
83         Initialise les attributs jdc, id, niveau et réalise les
84         enregistrements nécessaires
85         """
86         if self.parent:
87             self.jdc = self.parent.get_jdc_root()
88             self.id = self.parent.register(self)
89             self.niveau = None
90         else:
91             self.jdc = self.parent = None
92             self.id = None
93             self.niveau = None
94
95     def nettoiargs(self):
96         """
97            Cette methode a pour fonction de retirer tous les arguments egaux à None
98            de la liste des arguments. Ils sont supposés non présents et donc retirés.
99         """
100         for k in self.valeur.keys():
101             if self.valeur[k] == None:
102                 del self.valeur[k]
103
104     def McBuild(self):
105         """
106            Demande la construction des sous-objets et les stocke dans l'attribut
107            mc_liste.
108         """
109         self.mc_liste = self.build_mc()
110
111     def Build_sd(self, nom):
112         """
113            Construit le concept produit de l'opérateur. Deux cas
114            peuvent se présenter :
115
116              - le parent n'est pas défini. Dans ce cas, l'étape prend en charge la création
117                et le nommage du concept.
118
119              - le parent est défini. Dans ce cas, l'étape demande au parent la création et
120                le nommage du concept.
121
122         """
123         # message.debug(SUPERV, "Build_sd %s", self.nom)
124         self.sdnom = nom
125         try:
126             if self.parent:
127                 sd = self.parent.create_sdprod(self, nom)
128                 if type(self.definition.op_init) == types.FunctionType:
129                     apply(self.definition.op_init, (
130                         self, self.parent.g_context))
131             else:
132                 sd = self.get_sd_prod()
133                 # On n'utilise pas self.definition.op_init car self.parent
134                 # n'existe pas
135                 if sd != None and self.reuse == None:
136                     # On ne nomme le concept que dans le cas de non reutilisation
137                     # d un concept
138                     sd.set_name(nom)
139         except AsException, e:
140             raise AsException("Etape ", self.nom, 'ligne : ', self.appel[0],
141                               'fichier : ', self.appel[1], e)
142         except EOFError:
143             raise
144         except:
145             l = traceback.format_exception(
146                 sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])
147             raise AsException("Etape ", self.nom, 'ligne : ', self.appel[0],
148                               'fichier : ', self.appel[1] + '\n',
149                               string.join(l))
150
151         self.Execute()
152         return sd
153
154     def Execute(self):
155         """
156            Cette methode est un point d'entree prevu pour realiser une execution immediatement
157            apres avoir construit les mots cles et le concept produit.
158            Par defaut, elle ne fait rien. Elle doit etre surchargee dans une autre partie du programme.
159         """
160         return
161
162     def get_sd_prod(self):
163         """
164             Retourne le concept résultat de l'étape
165             Deux cas :
166                      - cas 1 : sd_prod de oper n'est pas une fonction
167                        il s'agit d'une sous classe de ASSD
168                        on construit le sd à partir de cette classe
169                        et on le retourne
170                      - cas 2 : il s'agit d'une fonction
171                        on l'évalue avec les mots-clés de l'étape (mc_liste)
172                        on construit le sd à partir de la classe obtenue
173                        et on le retourne
174         """
175         if type(self.definition.sd_prod) == types.FunctionType:
176             d = self.cree_dict_valeurs(self.mc_liste)
177             try:
178                 sd_prod = apply(self.definition.sd_prod, (), d)
179             except EOFError:
180                 raise
181             except Exception, exc:
182                 if CONTEXT.debug:
183                     traceback.print_exc()
184                 raise AsException("impossible d affecter un type au resultat:",
185                                   str(exc))
186         else:
187             sd_prod = self.definition.sd_prod
188         # on teste maintenant si la SD est réutilisée ou s'il faut la créer
189         if self.definition.reentrant != 'n' and self.reuse:
190             # Le concept produit est specifie reutilise (reuse=xxx). C'est une erreur mais non fatale.
191             # Elle sera traitee ulterieurement.
192             self.sd = self.reuse
193         else:
194             self.sd = sd_prod(etape=self)
195             # Si l'operateur est obligatoirement reentrant et reuse n'a pas ete specifie, c'est une erreur.
196             # On ne fait rien ici. L'erreur sera traiter par la suite.
197         # précaution
198         if self.sd is not None and not isinstance(self.sd, ASSD):
199             raise AsException("""
200 Impossible de typer le résultat !
201 Causes possibles :
202    Utilisateur : Soit la valeur fournie derrière "reuse" est incorrecte,
203                  soit il y a une "," à la fin d'une commande précédente.
204    Développeur : La fonction "sd_prod" retourne un type invalide.""")
205         return self.sd
206
207     def get_type_produit(self):
208         try:
209             return self.get_type_produit_brut()
210         except:
211             return None
212
213     def get_type_produit_brut(self):
214         """
215             Retourne le type du concept résultat de l'étape
216             Deux cas :
217               - cas 1 : sd_prod de oper n'est pas une fonction
218                 il s'agit d'une sous classe de ASSD
219                 on retourne le nom de la classe
220               - cas 2 : il s'agit d'une fonction
221                 on l'évalue avec les mots-clés de l'étape (mc_liste)
222                 et on retourne son résultat
223         """
224         if type(self.definition.sd_prod) == types.FunctionType:
225             d = self.cree_dict_valeurs(self.mc_liste)
226             sd_prod = apply(self.definition.sd_prod, (), d)
227         else:
228             sd_prod = self.definition.sd_prod
229         return sd_prod
230
231     def get_etape(self):
232         """
233            Retourne l'étape à laquelle appartient self
234            Un objet de la catégorie etape doit retourner self pour indiquer que
235            l'étape a été trouvée
236         """
237         return self
238
239     def supprime(self):
240         """
241            Méthode qui supprime toutes les références arrières afin que l'objet puisse
242            etre correctement détruit par le garbage collector
243         """
244         N_MCCOMPO.MCCOMPO.supprime(self)
245         self.jdc = None
246         self.appel = None
247         for name in dir(self):
248             if name.startswith('_cache_'):
249                 setattr(self, name, None)
250         if self.sd:
251             self.sd.supprime()
252
253     def __del__(self):
254         # message.debug(SUPERV, "__del__ ETAPE %s <%s>", getattr(self, 'nom', 'unknown'), self)
255         # if self.sd:
256             # message.debug(SUPERV, "            sd : %s", self.sd.nom)
257         pass
258
259     def get_created_sd(self):
260         """Retourne la liste des sd réellement produites par l'étape.
261         Si reuse est présent, `self.sd` a été créée 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'étape 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         # message.debug(SUPERV, "call etape.set_current_step", stack_id=-1)
279         cs = CONTEXT.get_current_step()
280         if self.parent != cs:
281             raise AsException("L'étape courante", cs.nom, cs,
282                               "devrait etre le parent de", self.nom, self)
283         else:
284             CONTEXT.unset_current_step()
285             CONTEXT.set_current_step(self)
286
287     def reset_current_step(self):
288         """
289               Methode utilisee par l'etape self qui remet son etape parent comme
290               etape courante
291         """
292         cs = CONTEXT.get_current_step()
293         if self != cs:
294             raise AsException("L'étape courante", cs.nom, cs,
295                               "devrait etre", self.nom, self)
296         else:
297             CONTEXT.unset_current_step()
298             CONTEXT.set_current_step(self.parent)
299
300     def issubstep(self, etape):
301         """
302             Cette methode retourne un entier indiquant si etape est une
303             sous etape de self ou non
304             1 = oui
305             0 = non
306             Une étape simple n'a pas de sous etape
307         """
308         return 0
309
310     def get_file(self, unite=None, fic_origine='', fname=None):
311         """
312             Retourne le nom du fichier correspondant à un numero d'unité
313             logique (entier) ainsi que le source contenu dans le fichier
314         """
315         if self.jdc:
316             return self.jdc.get_file(unite=unite, fic_origine=fic_origine, fname=fname)
317         else:
318             if unite != None:
319                 if os.path.exists("fort." + str(unite)):
320                     fname = "fort." + str(unite)
321             if fname == None:
322                 raise AsException("Impossible de trouver le fichier correspondant"
323                                   " a l unite %s" % unite)
324             if not os.path.exists(fname):
325                 raise AsException("%s n'est pas un fichier existant" % unite)
326             fproc = open(fname, 'r')
327             text = fproc.read()
328             fproc.close()
329             text = text.replace('\r\n', '\n')
330             linecache.cache[fname] = 0, 0, text.split('\n'), fname
331             return fname, text
332
333     def accept(self, visitor):
334         """
335            Cette methode permet de parcourir l'arborescence des objets
336            en utilisant le pattern VISITEUR
337         """
338         visitor.visitETAPE(self)
339
340     def update_context(self, d):
341         """
342             Cette methode doit updater le contexte fournit par
343             l'appelant en argument (d) en fonction de sa definition
344         """
345         if type(self.definition.op_init) == types.FunctionType:
346             apply(self.definition.op_init, (self, d))
347         if self.sd:
348             d[self.sd.nom] = self.sd
349
350     def copy(self):
351         """ Méthode qui retourne une copie de self non enregistrée auprès du JDC
352             et sans sd
353         """
354         etape = copy(self)
355         etape.sd = None
356         etape.state = 'modified'
357         etape.reuse = None
358         etape.sdnom = None
359         etape.etape = etape
360         etape.mc_liste = []
361         for objet in self.mc_liste:
362             new_obj = objet.copy()
363             new_obj.reparent(etape)
364             etape.mc_liste.append(new_obj)
365         return etape
366
367     def copy_reuse(self, old_etape):
368         """ Méthode qui copie le reuse d'une autre étape.
369         """
370         if hasattr(old_etape, "reuse"):
371             self.reuse = old_etape.reuse
372
373     def copy_sdnom(self, old_etape):
374         """ Méthode qui copie le sdnom d'une autre étape.
375         """
376         if hasattr(old_etape, "sdnom"):
377             self.sdnom = old_etape.sdnom
378
379     def reparent(self, parent):
380         """
381             Cette methode sert a reinitialiser la parente de l'objet
382         """
383         self.parent = parent
384         self.jdc = parent.get_jdc_root()
385         self.etape = self
386         for mocle in self.mc_liste:
387             mocle.reparent(self)
388         if self.sd and self.reuse == None:
389             self.sd.jdc = self.jdc
390
391     def get_cmd(self, nomcmd):
392         """
393             Méthode pour recuperer la definition d'une commande
394             donnee par son nom dans les catalogues declares
395             au niveau du jdc
396             Appele par un ops d'une macro en Python
397         """
398         return self.jdc.get_cmd(nomcmd)
399
400     def copy_intern(self, etape):
401         """
402             Méthode permettant lors du processus de recopie de copier
403             les elements internes d'une etape dans une autre
404         """
405         return
406
407     def full_copy(self, parent=None):
408         """
409            Méthode permettant d'effectuer une copie complète
410            d'une étape (y compris concept produit, éléments internes)
411            Si l'argument parent est fourni, la nouvelle étape
412            aura cet objet comme parent.
413         """
414         new_etape = self.copy()
415         new_etape.copy_reuse(self)
416         new_etape.copy_sdnom(self)
417         if parent:
418             new_etape.reparent(parent)
419         if self.sd:
420             new_sd = self.sd.__class__(etape=new_etape)
421             new_etape.sd = new_sd
422             if self.reuse == None:
423                 new_etape.parent.NommerSdprod(new_sd, self.sd.nom)
424             else:
425                 new_sd.set_name(self.sd.nom)
426         new_etape.copy_intern(self)
427         return new_etape
428
429     def reset_jdc(self, new_jdc):
430         """
431            Reinitialise le nommage du concept de l'etape lors d'un changement de jdc
432         """
433         if self.sd and self.reuse == None:
434             self.parent.NommerSdprod(self.sd, self.sd.nom)
435
436     def is_include(self):
437         """Permet savoir si on a affaire à la commande INCLUDE
438         car le comportement de ces macros est particulier.
439         """
440         return self.nom.startswith('INCLUDE')
441
442     def sd_accessible(self):
443         """Dit si on peut acceder aux "valeurs" (jeveux) de l'ASSD produite par l'étape.
444         """
445         if CONTEXT.debug:
446             print '`- ETAPE sd_accessible :', self.nom
447         return self.parent.sd_accessible()
448
449     def get_concept(self, nomsd):
450         """
451             Méthode pour recuperer un concept à partir de son nom
452         """
453         # pourrait être appelée par une commande fortran faisant appel à des fonctions python
454         # on passe la main au parent
455         return self.parent.get_concept(nomsd)