Salome HOME
868fac35563acd187d4d1e84b200ae8ca12c8935
[tools/eficas.git] / Noyau / N_JDC.py
1 #@ MODIF N_JDC Noyau  DATE 23/10/2002   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
103    def compile(self):
104       """
105          Cette methode compile la chaine procedure
106          Si des erreurs se produisent, elles sont consignées dans le 
107          compte-rendu self.cr
108       """
109       try:
110         if self.appli != None : 
111            self.appli.affiche_infos('Compilation du fichier de commandes \
112                                      en cours ...')
113         self.proc_compile=compile(self.procedure,self.nom,'exec')
114       except SyntaxError,e:
115         if CONTEXT.debug : traceback.print_exc()
116         l=traceback.format_exception_only(SyntaxError,e)
117         self.cr.exception("Compilation impossible : "+string.join(l))
118       return
119
120    def exec_compile(self):
121       """
122          Cette méthode execute le jeu de commandes compilé dans le contexte
123          self.g_context de l'objet JDC
124       """
125       CONTEXT.set_current_step(self)
126       # Le module nommage utilise le module linecache pour accéder
127       # au source des commandes du jeu de commandes.
128       # Dans le cas d'un fichier, on accède au contenu de ce fichier
129       # Dans le cas d'une chaine de caractères il faut accéder
130       # aux commandes qui sont dans la chaine
131       import linecache
132       linecache.cache[self.nom]=0,0,string.split(self.procedure,'\n'),self.nom
133       try:
134          exec self.exec_init in self.g_context
135          for obj_cata in self.cata:
136             if type(obj_cata) == types.ModuleType :
137                init2 = "from "+obj_cata.__name__+" import *"
138                exec init2 in self.g_context
139
140          # Initialisation du contexte global pour l'évaluation des conditions de BLOC
141          # On utilise une copie de l'initialisation du contexte du jdc
142          self.condition_context=self.g_context.copy()
143
144          # Si l'attribut context_ini n'est pas vide, on ajoute au contexte global
145          # le contexte initial (--> permet d'évaluer un JDC en récupérant un contexte
146          # d'un autre par exemple)
147          if self.context_ini :
148             self.g_context.update(self.context_ini)
149             # Update du dictionnaire des concepts
150             for sdnom,sd in self.context_ini.items():
151                if isinstance(sd,ASSD):self.sds_dict[sdnom]=sd
152
153          if self.appli != None : 
154             self.appli.affiche_infos('Interprétation du fichier de \
155                                       commandes en cours ...')
156          # On sauve le contexte pour garder la memoire des constantes
157          # En mode edition (EFICAS) ou lors des verifications le contexte 
158          # est recalculé
159          # mais les constantes sont perdues
160          self.const_context=self.g_context
161          exec self.proc_compile in self.g_context
162
163          CONTEXT.unset_current_step()
164          if self.appli != None : self.appli.affiche_infos('')
165
166       except EOFError:
167         # Exception utilise pour interrompre un jeu
168         # de commandes avant la fin
169         # Fonctionnement normal, ne doit pas etre considere comme une erreur
170         CONTEXT.unset_current_step()
171
172       except AsException,e:
173         # une erreur a ete identifiee
174         if CONTEXT.debug :
175           traceback.print_exc()
176         self.cr.exception(str(e))
177         CONTEXT.unset_current_step()
178
179       except NameError,e:
180         etype, value, tb = sys.exc_info()
181         l= traceback.extract_tb(tb)
182         s= traceback.format_exception_only("Erreur de nom",e)[0][:-1]
183         message = "erreur de syntaxe,  %s ligne %d" % (s,l[-1][1])
184         if CONTEXT.debug :
185           #prbanner(message)
186           traceback.print_exc()
187         self.cr.exception(message)
188         CONTEXT.unset_current_step()
189
190       except :
191         # erreur inattendue
192         # sys_exc_typ,sys_exc_value,sys_exc_frame = sys_exc.info() 
193         # (tuple de 3 éléments)
194         if CONTEXT.debug :
195           traceback.print_exc()
196           #prbanner("erreur non prevue et non traitee prevenir \
197           #           la maintenance "+self.nom)
198         l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],
199                                      sys.exc_info()[2])
200         self.cr.exception("erreur non prevue et non traitee prevenir la maintenance "+
201                            self.nom+'\n'+ string.join(l))
202         CONTEXT.unset_current_step()
203
204    def register(self,etape):
205       """
206          Cette méthode ajoute etape dans la liste des etapes : self.etapes
207          et retourne un numéro d'enregistrement
208       """
209       self.etapes.append(etape)
210       return self.g_register(etape)
211
212    def o_register(self,sd):
213       """
214          Retourne un identificateur pour concept
215       """
216       self.nsd=self.nsd+1
217       nom=sd.idracine + self.SEP + `self.nsd`
218       return nom
219
220    def g_register(self,etape):
221       """
222           Retourne un identificateur pour etape
223       """
224       self.nstep=self.nstep+1
225       idetape=etape.idracine + self.SEP + `self.nstep`
226       return idetape
227
228    def create_sdprod(self,etape,nomsd):
229       """ 
230           Intention : Cette methode doit fabriquer le concept produit retourne
231                   par l'etape etape et le nommer.
232                   Elle est appelée à l'initiative de l'etape
233                   pendant le processus de construction de cette etape : 
234                     methode __call__ de la classe CMD (OPER ou MACRO)
235                   Ce travail est réalisé par le contexte supérieur 
236                   (etape.parent) car dans certains cas, le concept ne doit 
237                   pas etre fabriqué mais l'etape doit simplement utiliser 
238                   un concept préexistant.
239                   Cas 1 : etape.reuse != None : le concept est réutilisé
240                   Cas 2 : l'étape appartient à une macro qui a déclaré un 
241                           concept de sortie qui doit etre produit par cette 
242                           etape.
243                   Dans le cas du JDC, le deuxième cas ne peut pas se produire.
244       """
245       sd= etape.get_sd_prod()
246       if sd != None and etape.reuse == None:
247          # ATTENTION : On ne nomme la SD que dans le cas de non reutilisation 
248          # d un concept
249          self.NommerSdprod(sd,nomsd)
250       return sd
251
252    def NommerSdprod(self,sd,sdnom,restrict='non'):
253       """ 
254           Nomme la SD apres avoir verifie que le nommage est possible : nom 
255           non utilise
256           Si le nom est deja utilise, leve une exception
257           Met le concept créé dans le concept global g_context
258       """
259       if CONTEXT.debug : print "JDC.NommerSdprod ",sd,sdnom
260       o=self.sds_dict.get(sdnom,None)
261       if isinstance(o,ASSD):
262          raise AsException("Nom de concept deja defini : %s" % sdnom)
263
264       # ATTENTION : Il ne faut pas ajouter sd dans sds car il s y trouve deja.
265       # Ajoute a la creation (appel de reg_sd).
266       self.sds_dict[sdnom]=sd
267       sd.nom=sdnom
268
269       # En plus si restrict vaut 'non', on insere le concept dans le contexte du JDC
270       if restrict == 'non':
271          self.g_context[sdnom]=sd
272
273    def reg_sd(self,sd):
274       """ 
275           Methode appelee dans l __init__ d un ASSD lors de sa creation 
276           pour s enregistrer
277       """
278       self.sds.append(sd)
279       return self.o_register(sd)
280
281    def delete_concept_after_etape(self,etape,sd):
282       """
283           Met à jour les étapes du JDC qui sont après etape suite à
284           la disparition du concept sd
285       """
286       # Cette methode est définie dans le noyau mais ne sert que pendant 
287       # la phase de creation des etapes et des concepts. Il n'y a aucun 
288       # traitement particulier à réaliser.
289       # Dans d'autres conditions, il faut surcharger cette méthode
290       return
291
292    def supprime(self):
293       N_OBJECT.OBJECT.supprime(self)
294       for etape in self.etapes:
295          etape.supprime()
296
297    def get_file(self,unite=None,fic_origine=''):
298       """
299           Retourne le nom du fichier correspondant à un numero d'unité 
300           logique (entier) ainsi que le source contenu dans le fichier
301       """
302       if self.appli :
303          # Si le JDC est relié à une application maitre, on délègue la recherche
304          file= self.appli.get_file(unite,fic_origine)
305       else:
306          file = None
307          if unite != None:
308             if os.path.exists("fort."+str(unite)):
309                file= "fort."+str(unite)
310          if file == None :
311             raise AsException("Impossible de trouver le fichier correspondant \
312                                a l unite %s" % unite)
313          if not os.path.exists(file):
314             raise AsException("%s n'est pas un fichier existant" % unite)
315       fproc=open(file,'r')
316       text=string.replace(fproc.read(),'\r\n','\n')
317       fproc.close()
318       linecache.cache[file]=0,0,string.split(text,'\n'),file
319       return file,text
320
321    def set_par_lot(self,par_lot):
322       """ 
323           Met le mode de traitement a PAR LOT 
324           ou a COMMANDE par COMMANDE
325           en fonction de la valeur du mot cle PAR_LOT et 
326           du contexte : application maitre ou pas
327       """
328       if self.appli == None:
329         # Pas d application maitre
330         self.par_lot=par_lot
331       else:
332         # Avec application maitre
333         self.par_lot='OUI'
334
335    def accept(self,visitor):
336       """
337          Cette methode permet de parcourir l'arborescence des objets
338          en utilisant le pattern VISITEUR
339       """
340       visitor.visitJDC(self)
341
342    def interact(self):
343       """
344           Cette methode a pour fonction d'ouvrir un interpreteur 
345           pour que l'utilisateur entre des commandes interactivement
346       """
347       CONTEXT.set_current_step(self)
348       try:
349          # Le module nommage utilise le module linecache pour accéder
350          # au source des commandes du jeu de commandes.
351          # Dans le cas d'un fichier, on accède au contenu de ce fichier
352          # Dans le cas de la console interactive, il faut pouvoir accéder
353          # aux commandes qui sont dans le buffer de la console
354          import linecache,code
355          console= code.InteractiveConsole(self.g_context,filename="<console>")
356          linecache.cache["<console>"]=0,0,console.buffer,"<console>"
357          banner="""***********************************************
358 *          Interpreteur interactif %s
359 ***********************************************""" % self.code
360          console.interact(banner)
361       finally:
362          console=None
363          CONTEXT.unset_current_step()
364
365    def get_contexte_avant(self,etape):
366       """
367          Retourne le dictionnaire des concepts connus avant etape
368          On tient compte des commandes qui modifient le contexte
369          comme DETRUIRE ou les macros
370          Si etape == None, on retourne le contexte en fin de JDC
371       """
372       # L'étape courante pour laquelle le contexte a été calculé est 
373       # mémorisée dans self.index_etape_courante
374       # XXX on pourrait faire mieux dans le cas PAR_LOT="NON" : en 
375       # mémorisant l'étape
376       # courante pendant le processus de construction des étapes.
377       # Si on insère des commandes (par ex, dans EFICAS), il faut préalablement
378       # remettre ce pointeur à 0
379       if etape:
380          index_etape=self.etapes.index(etape)
381       else:
382          index_etape=len(self.etapes)
383       if index_etape >= self.index_etape_courante:
384          # On calcule le contexte en partant du contexte existant
385          d=self.current_context
386          liste_etapes=self.etapes[self.index_etape_courante:index_etape]
387       else:
388          d=self.current_context={}
389          liste_etapes=self.etapes
390
391       for e in liste_etapes:
392          if e is etape:
393             break
394          if e.isactif():
395             e.update_context(d)
396       self.index_etape_courante=index_etape
397       return d
398
399    def get_global_contexte(self):
400       return self.g_context.copy()