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