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