]> SALOME platform Git repositories - tools/eficas.git/blob - Noyau/N_JDC.py
Salome HOME
CCAR: merge de la version 1.14 dans la branche principale
[tools/eficas.git] / Noyau / N_JDC.py
1 #@ MODIF N_JDC Noyau  DATE 01/04/2008   AUTEUR COURTOIS M.COURTOIS 
2 # -*- coding: iso-8859-1 -*-
3 #            CONFIGURATION MANAGEMENT OF EDF VERSION
4 # ======================================================================
5 # COPYRIGHT (C) 1991 - 2002  EDF R&D                  WWW.CODE-ASTER.ORG
6 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
7 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
8 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR   
9 # (AT YOUR OPTION) ANY LATER VERSION.                                 
10 #
11 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT 
12 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF          
13 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU    
14 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.                            
15 #
16 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE   
17 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,       
18 #    1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.      
19 #                                                                       
20 #                                                                       
21 # ======================================================================
22
23
24 """
25    Ce module contient la classe JDC qui sert à interpréter un jeu de commandes
26 """
27
28 # Modules Python
29 import os,string,traceback
30 import types,sys,linecache
31
32 # Modules EFICAS
33 import N_OBJECT
34 import N_CR
35 from N_Exception import AsException
36 from N_ASSD import ASSD
37
38
39
40
41 MemoryErrorMsg = """MemoryError :
42
43 En général, cette erreur se produit car la mémoire utilisée hors du fortran
44 (jeveux) est importante.
45
46 Causes possibles :
47    - le calcul produit de gros objets Python dans une macro-commande ou
48      dans le jeu de commande lui-même,
49    - le calcul appelle un solveur (MUMPS par exemple) ou un outil externe
50      qui a besoin de mémoire hors jeveux,
51    - utilisation de jeveux dynamique,
52    - ...
53
54 Solution :
55    - distinguer la mémoire limite du calcul (case "Mémoire totale" de astk)
56      de la mémoire réservée à jeveux (case "dont Aster"), le reste étant
57      disponible pour les allocations dynamiques.
58 """
59
60
61
62 class JDC(N_OBJECT.OBJECT):
63    """
64       Cette classe interprete un jeu de commandes fourni sous
65       la forme d'une chaine de caractères
66
67       Attributs de classe :
68
69       Attributs d'instance :
70
71    """
72    nature = "JDC"
73    CR=N_CR.CR
74    exec_init="""
75 import Accas
76 from Accas import _F
77 from Accas import *
78 NONE = None
79 """
80
81    from N_utils import SEP
82
83    def __init__(self,definition=None,procedure=None,cata=None,
84                      cata_ord_dico=None,parent=None,
85                      nom='SansNom',appli=None,context_ini=None,**args):
86       self.procedure=procedure
87       self.definition = definition
88       self.cata=cata
89       if type(self.cata) != types.TupleType and cata != None: 
90          self.cata=(self.cata,)
91       self.cata_ordonne_dico=cata_ord_dico
92       self.nom = nom
93       self.appli=appli
94       self.parent=parent
95       self.context_ini=context_ini
96       # On conserve les arguments supplémentaires. Il est possible de passer 
97       # des informations globales au JDC par ce moyen. Il pourrait etre plus 
98       # sur de mettre en place le mecanisme des mots-cles pour verifier la 
99       # validité des valeurs passées.
100       # Ceci reste à faire
101       # On initialise avec les parametres de la definition puis on 
102       # update avec ceux du JDC
103       self.args=self.definition.args
104       self.args.update(args)
105       self.nstep=0
106       self.nsd=0
107       self.par_lot='OUI'
108       if definition:
109          self.regles=definition.regles
110          self.code = definition.code
111       else:
112          self.regles=()
113          self.code = "CODE"
114       #
115       #  Creation de l objet compte rendu pour collecte des erreurs
116       #
117       self.cr = self.CR(debut = "CR phase d'initialisation", 
118                         fin = "fin CR phase d'initialisation")
119       # on met le jdc lui-meme dans le context global pour l'avoir sous
120       # l'etiquette "jdc" dans le fichier de commandes
121       self.g_context={ 'jdc' : self }
122       # Liste pour stocker tous les concepts produits dans le JDC
123       self.sds=[]
124       # Dictionnaire pour stocker tous les concepts du JDC (acces rapide par le nom)
125       self.sds_dict={}
126       self.etapes=[]
127       self.index_etapes = {}
128       self.mc_globaux={}
129       self.current_context={}
130       self.condition_context={}
131       self.index_etape_courante=0
132       self.UserError="UserError"
133       self.alea = None
134
135    def compile(self):
136       """
137          Cette methode compile la chaine procedure
138          Si des erreurs se produisent, elles sont consignées dans le 
139          compte-rendu self.cr
140       """
141       try:
142          if self.appli != None : 
143             self.appli.affiche_infos('Compilation du fichier de commandes en cours ...')
144          self.proc_compile=compile(self.procedure,self.nom,'exec')
145       except SyntaxError, e:
146          if CONTEXT.debug : traceback.print_exc()
147          l=traceback.format_exception_only(SyntaxError,e)
148          self.cr.exception("Compilation impossible : "+string.join(l))
149       except MemoryError, e:
150          self.cr.exception(MemoryErrorMsg)
151       except SystemError, e:
152          erreurs_connues = """
153 Causes possibles :
154  - offset too large : liste trop longue derrière un mot-clé.
155    Solution : liste = (valeurs, ..., )
156               MOT_CLE = *liste,
157 """
158          l=traceback.format_exception_only(SystemError,e)
159          l.append(erreurs_connues)
160          self.cr.exception("Compilation impossible : " + ''.join(l))
161       return
162
163    def exec_compile(self):
164       """
165          Cette méthode execute le jeu de commandes compilé dans le contexte
166          self.g_context de l'objet JDC
167       """
168       CONTEXT.set_current_step(self)
169       # Le module nommage utilise le module linecache pour accéder
170       # au source des commandes du jeu de commandes.
171       # Dans le cas d'un fichier, on accède au contenu de ce fichier
172       # Dans le cas d'une chaine de caractères il faut accéder
173       # aux commandes qui sont dans la chaine
174       import linecache
175       linecache.cache[self.nom]=0,0,string.split(self.procedure,'\n'),self.nom
176       try:
177          exec self.exec_init in self.g_context
178          for obj_cata in self.cata:
179             if type(obj_cata) == types.ModuleType :
180                init2 = "from "+obj_cata.__name__+" import *"
181                exec init2 in self.g_context
182
183          # Initialisation du contexte global pour l'évaluation des conditions de BLOC
184          # On utilise une copie de l'initialisation du contexte du jdc
185          self.condition_context=self.g_context.copy()
186
187          # Si l'attribut context_ini n'est pas vide, on ajoute au contexte global
188          # le contexte initial (--> permet d'évaluer un JDC en récupérant un contexte
189          # d'un autre par exemple)
190          if self.context_ini :
191             self.g_context.update(self.context_ini)
192             # Update du dictionnaire des concepts
193             for sdnom,sd in self.context_ini.items():
194                if isinstance(sd,ASSD):self.sds_dict[sdnom]=sd
195
196          if self.appli != None : 
197             self.appli.affiche_infos('Interprétation du fichier de commandes en cours ...')
198          # On sauve le contexte pour garder la memoire des constantes
199          # En mode edition (EFICAS) ou lors des verifications le contexte 
200          # est recalculé
201          # mais les constantes sont perdues
202          self.const_context=self.g_context
203          exec self.proc_compile in self.g_context
204
205          CONTEXT.unset_current_step()
206          if self.appli != None : self.appli.affiche_infos('')
207
208       except EOFError:
209         # Exception utilise pour interrompre un jeu
210         # de commandes avant la fin
211         # Fonctionnement normal, ne doit pas etre considere comme une erreur
212         CONTEXT.unset_current_step()
213         self.affiche_fin_exec()
214         self.traiter_fin_exec('commande')
215
216       except AsException,e:
217         # une erreur a ete identifiee
218         if CONTEXT.debug :
219           traceback.print_exc()
220         # l'exception a été récupérée avant (où, comment ?),
221         # donc on cherche dans le texte
222         txt = str(e)
223         if txt.find('MemoryError') >= 0:
224            txt = MemoryErrorMsg
225         self.cr.exception(txt)
226         CONTEXT.unset_current_step()
227
228       except NameError,e:
229         etype, value, tb = sys.exc_info()
230         l= traceback.extract_tb(tb)
231         s= traceback.format_exception_only("Erreur de nom",e)[0][:-1]
232         message = "erreur de syntaxe,  %s ligne %d" % (s,l[-1][1])
233         if CONTEXT.debug :
234           traceback.print_exc()
235         self.cr.exception(message)
236         CONTEXT.unset_current_step()
237
238       except self.UserError,exc_val:
239         self.traiter_user_exception(exc_val)
240         CONTEXT.unset_current_step()
241         self.affiche_fin_exec()
242         self.traiter_fin_exec('commande')
243     
244       except :
245         # erreur inattendue
246         # sys_exc_typ,sys_exc_value,sys_exc_frame = sys_exc.info() 
247         # (tuple de 3 éléments)
248         if CONTEXT.debug : traceback.print_exc()
249
250         exc_typ,exc_val,exc_fr=sys.exc_info()
251         l=traceback.format_exception(exc_typ,exc_val,exc_fr)
252         self.cr.exception("erreur non prevue et non traitee prevenir la maintenance "+
253                            self.nom+'\n'+ string.join(l))
254         del exc_typ,exc_val,exc_fr
255         CONTEXT.unset_current_step()
256
257    def affiche_fin_exec(self):
258        """
259           Cette methode realise l'affichage final des statistiques de temps
260           apres l'execution de toutes
261           les commandes en mode commande par commande ou par lot
262           Elle doit etre surchargee pour en introduire un
263        """
264        return
265
266    def traiter_fin_exec(self,mode,etape=None):
267        """
268           Cette methode realise un traitement final apres l'execution de toutes
269           les commandes en mode commande par commande ou par lot
270           Par defaut il n'y a pas de traitement. Elle doit etre surchargee
271           pour en introduire un
272        """
273        print "FIN D'EXECUTION",mode,etape
274
275    def traiter_user_exception(self,exc_val):
276        """Cette methode realise un traitement sur les exceptions utilisateur    
277           Par defaut il n'y a pas de traitement. La méthode doit etre 
278           surchargée pour en introduire un.
279        """
280        return 
281
282    def register(self,etape):
283       """
284          Cette méthode ajoute etape dans la liste des etapes : self.etapes
285          et retourne un numéro d'enregistrement
286       """
287       self.etapes.append(etape)
288       self.index_etapes[etape] = len(self.etapes) - 1
289       return self.g_register(etape)
290
291    def o_register(self,sd):
292       """
293          Retourne un identificateur pour concept
294       """
295       self.nsd=self.nsd+1
296       nom=sd.idracine + self.SEP + `self.nsd`
297       return nom
298
299    def g_register(self,etape):
300       """
301           Retourne un identificateur pour etape
302       """
303       self.nstep=self.nstep+1
304       idetape=etape.idracine + self.SEP + `self.nstep`
305       return idetape
306
307    def create_sdprod(self,etape,nomsd):
308       """ 
309           Cette methode doit fabriquer le concept produit retourne
310           par l'etape etape et le nommer.
311
312           Elle est appelée à l'initiative de l'etape
313           pendant le processus de construction de cette etape : 
314           methode __call__ de la classe CMD (OPER ou MACRO)
315
316           Ce travail est réalisé par le contexte supérieur 
317           (etape.parent) car dans certains cas, le concept ne doit 
318           pas etre fabriqué mais l'etape doit simplement utiliser 
319           un concept préexistant.
320
321           Deux cas possibles :
322                   - Cas 1 : etape.reuse != None : le concept est réutilisé
323                   - Cas 2 : l'étape appartient à une macro qui a déclaré un 
324                           concept de sortie qui doit etre produit par cette 
325                           etape.
326           Dans le cas du JDC, le deuxième cas ne peut pas se produire.
327       """
328       sd= etape.get_sd_prod()
329       if sd != None and (etape.definition.reentrant == 'n' or etape.reuse is None) :
330          # ATTENTION : On ne nomme la SD que dans le cas de non reutilisation 
331          # d un concept. Commande non reentrante ou reuse absent.
332          self.NommerSdprod(sd,nomsd)
333       return sd
334
335    def NommerSdprod(self,sd,sdnom,restrict='non'):
336       """ 
337           Nomme la SD apres avoir verifie que le nommage est possible : nom 
338           non utilise
339           Si le nom est deja utilise, leve une exception
340           Met le concept créé dans le concept global g_context
341       """
342       if CONTEXT.debug : print "JDC.NommerSdprod ",sd,sdnom
343
344       o=self.sds_dict.get(sdnom,None)
345       if isinstance(o,ASSD):
346          raise AsException("Nom de concept deja defini : %s" % sdnom)
347
348       # ATTENTION : Il ne faut pas ajouter sd dans sds car il s y trouve deja.
349       # Ajoute a la creation (appel de reg_sd).
350       self.sds_dict[sdnom]=sd
351       sd.set_name(sdnom)
352
353       # En plus si restrict vaut 'non', on insere le concept dans le contexte du JDC
354       if restrict == 'non':
355          self.g_context[sdnom]=sd
356
357    def reg_sd(self,sd):
358       """ 
359           Methode appelee dans l __init__ d un ASSD lors de sa creation 
360           pour s enregistrer
361       """
362       self.sds.append(sd)
363       return self.o_register(sd)
364
365    def delete_concept_after_etape(self,etape,sd):
366       """
367           Met à jour les étapes du JDC qui sont après etape suite à
368           la disparition du concept sd
369       """
370       # Cette methode est définie dans le noyau mais ne sert que pendant 
371       # la phase de creation des etapes et des concepts. Il n'y a aucun 
372       # traitement particulier à réaliser.
373       # Dans d'autres conditions, il faut surcharger cette méthode
374       return
375
376    def supprime(self):
377       N_OBJECT.OBJECT.supprime(self)
378       for etape in self.etapes:
379          etape.supprime()
380
381    def get_file(self,unite=None,fic_origine=''):
382       """
383           Retourne le nom du fichier correspondant à un numero d'unité 
384           logique (entier) ainsi que le source contenu dans le fichier
385       """
386       if self.appli :
387          # Si le JDC est relié à une application maitre, on délègue la recherche
388          file,text= self.appli.get_file(unite,fic_origine)
389       else:
390          file = None
391          if unite != None:
392             if os.path.exists("fort."+str(unite)):
393                file= "fort."+str(unite)
394          if file == None :
395             raise AsException("Impossible de trouver le fichier correspondant"
396                                " a l unite %s" % unite)
397          if not os.path.exists(file):
398             raise AsException("%s n'est pas un fichier existant" % unite)
399          fproc=open(file,'r')
400          text=fproc.read()
401          fproc.close()
402       if file == None : return None,None
403       text=string.replace(text,'\r\n','\n')
404       linecache.cache[file]=0,0,string.split(text,'\n'),file
405       return file,text
406
407    def set_par_lot(self,par_lot):
408       """ 
409           Met le mode de traitement a PAR LOT 
410           ou a COMMANDE par COMMANDE
411           en fonction de la valeur du mot cle PAR_LOT et 
412           du contexte : application maitre ou pas
413       """
414       if self.appli == None:
415         # Pas d application maitre
416         self.par_lot=par_lot
417       else:
418         # Avec application maitre
419         self.par_lot='OUI'
420
421    def accept(self,visitor):
422       """
423          Cette methode permet de parcourir l'arborescence des objets
424          en utilisant le pattern VISITEUR
425       """
426       visitor.visitJDC(self)
427
428    def interact(self):
429       """
430           Cette methode a pour fonction d'ouvrir un interpreteur 
431           pour que l'utilisateur entre des commandes interactivement
432       """
433       CONTEXT.set_current_step(self)
434       try:
435          # Le module nommage utilise le module linecache pour accéder
436          # au source des commandes du jeu de commandes.
437          # Dans le cas d'un fichier, on accède au contenu de ce fichier
438          # Dans le cas de la console interactive, il faut pouvoir accéder
439          # aux commandes qui sont dans le buffer de la console
440          import linecache,code
441          console= code.InteractiveConsole(self.g_context,filename="<console>")
442          linecache.cache["<console>"]=0,0,console.buffer,"<console>"
443          banner="""***********************************************
444 *          Interpreteur interactif %s
445 ***********************************************""" % self.code
446          console.interact(banner)
447       finally:
448          console=None
449          CONTEXT.unset_current_step()
450
451    def get_contexte_avant(self,etape):
452       """
453          Retourne le dictionnaire des concepts connus avant etape
454          On tient compte des commandes qui modifient le contexte
455          comme DETRUIRE ou les macros
456          Si etape == None, on retourne le contexte en fin de JDC
457       """
458       # L'étape courante pour laquelle le contexte a été calculé est 
459       # mémorisée dans self.index_etape_courante
460       # XXX on pourrait faire mieux dans le cas PAR_LOT="NON" : en 
461       # mémorisant l'étape
462       # courante pendant le processus de construction des étapes.
463       # Si on insère des commandes (par ex, dans EFICAS), il faut préalablement
464       # remettre ce pointeur à 0
465       if etape:
466          index_etape = self.index_etapes[etape]
467       else:
468          index_etape=len(self.etapes)
469       if index_etape >= self.index_etape_courante:
470          # On calcule le contexte en partant du contexte existant
471          d=self.current_context
472          if self.index_etape_courante==0 and self.context_ini:
473             d.update(self.context_ini)
474          liste_etapes=self.etapes[self.index_etape_courante:index_etape]
475       else:
476          d=self.current_context={}
477          if self.context_ini:
478             d.update(self.context_ini)
479          liste_etapes=self.etapes
480
481       for e in liste_etapes:
482          if e is etape:
483             break
484          if e.isactif():
485             e.update_context(d)
486       self.index_etape_courante=index_etape
487       return d
488
489    def get_global_contexte(self):
490       return self.g_context.copy()
491
492    def get_cmd(self,nomcmd):
493       """
494           Méthode pour recuperer la definition d'une commande
495           donnee par son nom dans les catalogues declares
496           au niveau du jdc
497       """
498       for cata in self.cata:
499           if hasattr(cata,nomcmd):
500              return getattr(cata,nomcmd)
501
502    def append_reset(self,etape):
503        """
504           Ajoute une etape provenant d'un autre jdc a la liste des etapes
505           et remet à jour la parenté de l'étape et des concepts
506        """
507        self.etapes.append(etape)
508        self.index_etapes[etape] = len(self.etapes) - 1
509        etape.reparent(self)
510        etape.reset_jdc(self)