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