Salome HOME
version 19 mars
[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:
182                 if CONTEXT.debug:
183                     traceback.print_exc()
184                 l = traceback.format_exception(
185                     sys.exc_info()[0], sys.exc_info()[1],
186                     sys.exc_info()[2])
187                 raise AsException("impossible d affecter un type au resultat",
188                                   string.join(l[2:]))
189                 #         sys.exc_info()[0],sys.exc_info()[1],)
190         else:
191             sd_prod = self.definition.sd_prod
192         # on teste maintenant si la SD est réutilisée ou s'il faut la créer
193         if self.definition.reentrant != 'n' and self.reuse:
194             # Le concept produit est specifie reutilise (reuse=xxx). C'est une erreur mais non fatale.
195             # Elle sera traitee ulterieurement.
196             self.sd = self.reuse
197         else:
198             self.sd = sd_prod(etape=self)
199             # Si l'operateur est obligatoirement reentrant et reuse n'a pas ete specifie, c'est une erreur.
200             # On ne fait rien ici. L'erreur sera traiter par la suite.
201         # précaution
202         if self.sd is not None and not isinstance(self.sd, ASSD):
203             raise AsException("""
204 Impossible de typer le résultat !
205 Causes possibles :
206    Utilisateur : Soit la valeur fournie derrière "reuse" est incorrecte,
207                  soit il y a une "," à la fin d'une commande précédente.
208    Développeur : La fonction "sd_prod" retourne un type invalide.""")
209         return self.sd
210
211     def get_type_produit(self):
212         try:
213             return self.get_type_produit_brut()
214         except:
215             return None
216
217     def get_type_produit_brut(self):
218         """
219             Retourne le type du concept résultat de l'étape
220             Deux cas :
221               - cas 1 : sd_prod de oper n'est pas une fonction
222                 il s'agit d'une sous classe de ASSD
223                 on retourne le nom de la classe
224               - cas 2 : il s'agit d'une fonction
225                 on l'évalue avec les mots-clés de l'étape (mc_liste)
226                 et on retourne son résultat
227         """
228         if type(self.definition.sd_prod) == types.FunctionType:
229             d = self.cree_dict_valeurs(self.mc_liste)
230             sd_prod = apply(self.definition.sd_prod, (), d)
231         else:
232             sd_prod = self.definition.sd_prod
233         return sd_prod
234
235     def get_etape(self):
236         """
237            Retourne l'étape à laquelle appartient self
238            Un objet de la catégorie etape doit retourner self pour indiquer que
239            l'étape a été trouvée
240         """
241         return self
242
243     def supprime(self):
244         """
245            Méthode qui supprime toutes les références arrières afin que l'objet puisse
246            etre correctement détruit par le garbage collector
247         """
248         N_MCCOMPO.MCCOMPO.supprime(self)
249         self.jdc = None
250         self.appel = None
251         for name in dir(self):
252             if name.startswith('_cache_'):
253                 setattr(self, name, None)
254         if self.sd:
255             self.sd.supprime()
256
257     def __del__(self):
258         # message.debug(SUPERV, "__del__ ETAPE %s <%s>", getattr(self, 'nom', 'unknown'), self)
259         # if self.sd:
260             # message.debug(SUPERV, "            sd : %s", self.sd.nom)
261         pass
262
263     def get_created_sd(self):
264         """Retourne la liste des sd réellement produites par l'étape.
265         Si reuse est présent, `self.sd` a été créée avant, donc n'est pas dans
266         cette liste."""
267         if not self.reuse and self.sd:
268             return [self.sd, ]
269         return []
270
271     def isactif(self):
272         """
273            Indique si l'étape est active (1) ou inactive (0)
274         """
275         return self.actif
276
277     def set_current_step(self):
278         """
279             Methode utilisee pour que l etape self se declare etape
280             courante. Utilise par les macros
281         """
282         # message.debug(SUPERV, "call etape.set_current_step", stack_id=-1)
283         cs = CONTEXT.get_current_step()
284         if self.parent != cs:
285             raise AsException("L'étape courante", cs.nom, cs,
286                               "devrait etre le parent de", self.nom, self)
287         else:
288             CONTEXT.unset_current_step()
289             CONTEXT.set_current_step(self)
290
291     def reset_current_step(self):
292         """
293               Methode utilisee par l'etape self qui remet son etape parent comme
294               etape courante
295         """
296         cs = CONTEXT.get_current_step()
297         if self != cs:
298             raise AsException("L'étape courante", cs.nom, cs,
299                               "devrait etre", self.nom, self)
300         else:
301             CONTEXT.unset_current_step()
302             CONTEXT.set_current_step(self.parent)
303
304     def issubstep(self, etape):
305         """
306             Cette methode retourne un entier indiquant si etape est une
307             sous etape de self ou non
308             1 = oui
309             0 = non
310             Une étape simple n'a pas de sous etape
311         """
312         return 0
313
314     def get_file(self, unite=None, fic_origine='', fname=None):
315         """
316             Retourne le nom du fichier correspondant à un numero d'unité
317             logique (entier) ainsi que le source contenu dans le fichier
318         """
319         if self.jdc:
320             return self.jdc.get_file(unite=unite, fic_origine=fic_origine, fname=fname)
321         else:
322             if unite != None:
323                 if os.path.exists("fort." + str(unite)):
324                     fname = "fort." + str(unite)
325             if fname == None:
326                 raise AsException("Impossible de trouver le fichier correspondant"
327                                   " a l unite %s" % unite)
328             if not os.path.exists(fname):
329                 raise AsException("%s n'est pas un fichier existant" % unite)
330             fproc = open(fname, 'r')
331             text = fproc.read()
332             fproc.close()
333             text = text.replace('\r\n', '\n')
334             linecache.cache[fname] = 0, 0, text.split('\n'), fname
335             return fname, text
336
337     def accept(self, visitor):
338         """
339            Cette methode permet de parcourir l'arborescence des objets
340            en utilisant le pattern VISITEUR
341         """
342         visitor.visitETAPE(self)
343
344     def update_context(self, d):
345         """
346             Cette methode doit updater le contexte fournit par
347             l'appelant en argument (d) en fonction de sa definition
348         """
349         if type(self.definition.op_init) == types.FunctionType:
350             apply(self.definition.op_init, (self, d))
351         if self.sd:
352             d[self.sd.nom] = self.sd
353
354     def copy(self):
355         """ Méthode qui retourne une copie de self non enregistrée auprès du JDC
356             et sans sd
357         """
358         etape = copy(self)
359         etape.sd = None
360         etape.state = 'modified'
361         etape.reuse = None
362         etape.sdnom = None
363         etape.etape = etape
364         etape.mc_liste = []
365         for objet in self.mc_liste:
366             new_obj = objet.copy()
367             new_obj.reparent(etape)
368             etape.mc_liste.append(new_obj)
369         return etape
370
371     def copy_reuse(self, old_etape):
372         """ Méthode qui copie le reuse d'une autre étape.
373         """
374         if hasattr(old_etape, "reuse"):
375             self.reuse = old_etape.reuse
376
377     def copy_sdnom(self, old_etape):
378         """ Méthode qui copie le sdnom d'une autre étape.
379         """
380         if hasattr(old_etape, "sdnom"):
381             self.sdnom = old_etape.sdnom
382
383     def reparent(self, parent):
384         """
385             Cette methode sert a reinitialiser la parente de l'objet
386         """
387         self.parent = parent
388         self.jdc = parent.get_jdc_root()
389         self.etape = self
390         for mocle in self.mc_liste:
391             mocle.reparent(self)
392         if self.sd and self.reuse == None:
393             self.sd.jdc = self.jdc
394
395     def get_cmd(self, nomcmd):
396         """
397             Méthode pour recuperer la definition d'une commande
398             donnee par son nom dans les catalogues declares
399             au niveau du jdc
400             Appele par un ops d'une macro en Python
401         """
402         return self.jdc.get_cmd(nomcmd)
403
404     def copy_intern(self, etape):
405         """
406             Méthode permettant lors du processus de recopie de copier
407             les elements internes d'une etape dans une autre
408         """
409         return
410
411     def full_copy(self, parent=None):
412         """
413            Méthode permettant d'effectuer une copie complète
414            d'une étape (y compris concept produit, éléments internes)
415            Si l'argument parent est fourni, la nouvelle étape
416            aura cet objet comme parent.
417         """
418         new_etape = self.copy()
419         new_etape.copy_reuse(self)
420         new_etape.copy_sdnom(self)
421         if parent:
422             new_etape.reparent(parent)
423         if self.sd:
424             new_sd = self.sd.__class__(etape=new_etape)
425             new_etape.sd = new_sd
426             if self.reuse == None:
427                 new_etape.parent.NommerSdprod(new_sd, self.sd.nom)
428             else:
429                 new_sd.set_name(self.sd.nom)
430         new_etape.copy_intern(self)
431         return new_etape
432
433     def reset_jdc(self, new_jdc):
434         """
435            Reinitialise le nommage du concept de l'etape lors d'un changement de jdc
436         """
437         if self.sd and self.reuse == None:
438             self.parent.NommerSdprod(self.sd, self.sd.nom)
439
440     def is_include(self):
441         """Permet savoir si on a affaire à la commande INCLUDE
442         car le comportement de ces macros est particulier.
443         """
444         return self.nom.startswith('INCLUDE')
445
446     def sd_accessible(self):
447         """Dit si on peut acceder aux "valeurs" (jeveux) de l'ASSD produite par l'étape.
448         """
449         if CONTEXT.debug:
450             print '`- ETAPE sd_accessible :', self.nom
451         return self.parent.sd_accessible()
452
453     def get_concept(self, nomsd):
454         """
455             Méthode pour recuperer un concept à partir de son nom
456         """
457         # pourrait être appelée par une commande fortran faisant appel à des fonctions python
458         # on passe la main au parent
459         return self.parent.get_concept(nomsd)