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