1 #@ MODIF N_JDC Noyau DATE 25/10/2011 AUTEUR COURTOIS M.COURTOIS
2 # -*- coding: iso-8859-1 -*-
3 # RESPONSABLE COURTOIS M.COURTOIS
4 # CONFIGURATION MANAGEMENT OF EDF VERSION
5 # ======================================================================
6 # COPYRIGHT (C) 1991 - 2011 EDF R&D WWW.CODE-ASTER.ORG
7 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
8 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
9 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
10 # (AT YOUR OPTION) ANY LATER VERSION.
12 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
13 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
14 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
15 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.
17 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
18 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
19 # 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
22 # ======================================================================
26 Ce module contient la classe JDC qui sert à interpréter un jeu de commandes
30 import os,string,traceback
31 import types,sys,linecache
36 from N_Exception import AsException
37 from N_ASSD import ASSD
38 from N_info import message, SUPERV
41 MemoryErrorMsg = """MemoryError :
43 En général, cette erreur se produit car la mémoire utilisée hors du fortran
44 (jeveux) est importante.
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,
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.
62 class JDC(N_OBJECT.OBJECT):
64 Cette classe interprete un jeu de commandes fourni sous
65 la forme d'une chaine de caractères
69 Attributs d'instance :
81 from N_utils import SEP
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
89 if type(self.cata) != types.TupleType and cata != None:
90 self.cata=(self.cata,)
91 self._build_reserved_kw_list()
92 self.cata_ordonne_dico=cata_ord_dico
96 self.context_ini=context_ini
97 # On conserve les arguments supplémentaires. Il est possible de passer
98 # des informations globales au JDC par ce moyen. Il pourrait etre plus
99 # sur de mettre en place le mecanisme des mots-cles pour verifier la
100 # validité des valeurs passées.
102 # On initialise avec les parametres de la definition puis on
103 # update avec ceux du JDC
104 self.args=self.definition.args
105 self.args.update(args)
110 self.regles=definition.regles
111 self.code = definition.code
116 # Creation de l objet compte rendu pour collecte des erreurs
118 self.cr = self.CR(debut = "CR phase d'initialisation",
119 fin = "fin CR phase d'initialisation")
120 # on met le jdc lui-meme dans le context global pour l'avoir sous
121 # l'etiquette "jdc" dans le fichier de commandes
122 self.g_context={ 'jdc' : self }
123 # Liste pour stocker tous les concepts produits dans le JDC
125 # Dictionnaire pour stocker tous les concepts du JDC (acces rapide par le nom)
128 self.index_etapes = {}
130 self.current_context={}
131 self.condition_context={}
132 self.index_etape_courante=0
133 self.UserError="UserError"
138 Cette methode compile la chaine procedure
139 Si des erreurs se produisent, elles sont consignées dans le
143 if self.appli != None :
144 self.appli.affiche_infos('Compilation du fichier de commandes en cours ...')
145 self.proc_compile=compile(self.procedure,self.nom,'exec')
146 except SyntaxError, e:
147 if CONTEXT.debug : traceback.print_exc()
148 l=traceback.format_exception_only(SyntaxError,e)
149 self.cr.exception("Compilation impossible : "+string.join(l))
150 except MemoryError, e:
151 self.cr.exception(MemoryErrorMsg)
152 except SystemError, e:
153 erreurs_connues = """
155 - offset too large : liste trop longue derrière un mot-clé.
156 Solution : liste = (valeurs, ..., )
159 l=traceback.format_exception_only(SystemError,e)
160 l.append(erreurs_connues)
161 self.cr.exception("Compilation impossible : " + ''.join(l))
164 def exec_compile(self):
166 Cette méthode execute le jeu de commandes compilé dans le contexte
167 self.g_context de l'objet JDC
169 CONTEXT.set_current_step(self)
170 # Le module nommage utilise le module linecache pour accéder
171 # au source des commandes du jeu de commandes.
172 # Dans le cas d'un fichier, on accède au contenu de ce fichier
173 # Dans le cas d'une chaine de caractères il faut accéder
174 # aux commandes qui sont dans la chaine
176 linecache.cache[self.nom]=0,0,string.split(self.procedure,'\n'),self.nom
178 exec self.exec_init in self.g_context
179 for obj_cata in self.cata:
180 if type(obj_cata) == types.ModuleType :
181 init2 = "from "+obj_cata.__name__+" import *"
182 exec init2 in self.g_context
184 # Initialisation du contexte global pour l'évaluation des conditions de BLOC
185 # On utilise une copie de l'initialisation du contexte du jdc
186 self.condition_context=self.g_context.copy()
188 # Si l'attribut context_ini n'est pas vide, on ajoute au contexte global
189 # le contexte initial (--> permet d'évaluer un JDC en récupérant un contexte
190 # d'un autre par exemple)
191 if self.context_ini :
192 self.g_context.update(self.context_ini)
193 # Update du dictionnaire des concepts
194 for sdnom,sd in self.context_ini.items():
195 if isinstance(sd,ASSD):self.sds_dict[sdnom]=sd
197 if self.appli != None :
198 self.appli.affiche_infos('Interprétation du fichier de commandes en cours ...')
199 # On sauve le contexte pour garder la memoire des constantes
200 # En mode edition (EFICAS) ou lors des verifications le contexte
202 # mais les constantes sont perdues
203 self.const_context=self.g_context
204 message.debug(SUPERV, "pass")
205 exec self.proc_compile in self.g_context
207 CONTEXT.unset_current_step()
208 if self.appli != None : self.appli.affiche_infos('')
211 # Exception utilise pour interrompre un jeu
212 # de commandes avant la fin
213 # Fonctionnement normal, ne doit pas etre considere comme une erreur
214 CONTEXT.unset_current_step()
215 self.affiche_fin_exec()
216 self.traiter_fin_exec('commande')
218 except AsException,e:
219 # une erreur a ete identifiee
221 traceback.print_exc()
222 # l'exception a été récupérée avant (où, comment ?),
223 # donc on cherche dans le texte
225 if txt.find('MemoryError') >= 0:
227 self.cr.exception(txt)
228 CONTEXT.unset_current_step()
231 etype, value, tb = sys.exc_info()
232 l= traceback.extract_tb(tb)
233 s= traceback.format_exception_only("Erreur de nom",e)[0][:-1]
234 msg = "erreur de syntaxe, %s ligne %d" % (s,l[-1][1])
236 traceback.print_exc()
237 self.cr.exception(msg)
238 CONTEXT.unset_current_step()
240 except self.UserError,exc_val:
241 self.traiter_user_exception(exc_val)
242 CONTEXT.unset_current_step()
243 self.affiche_fin_exec()
244 self.traiter_fin_exec('commande')
248 # sys_exc_typ,sys_exc_value,sys_exc_frame = sys_exc.info()
249 # (tuple de 3 éléments)
250 if CONTEXT.debug : traceback.print_exc()
252 exc_typ,exc_val,exc_fr=sys.exc_info()
253 l=traceback.format_exception(exc_typ,exc_val,exc_fr)
254 self.cr.exception("erreur non prevue et non traitee prevenir la maintenance "+
255 self.nom+'\n'+ string.join(l))
256 del exc_typ,exc_val,exc_fr
257 CONTEXT.unset_current_step()
259 def affiche_fin_exec(self):
261 Cette methode realise l'affichage final des statistiques de temps
262 apres l'execution de toutes
263 les commandes en mode commande par commande ou par lot
264 Elle doit etre surchargee pour en introduire un
268 def traiter_fin_exec(self,mode,etape=None):
270 Cette methode realise un traitement final apres l'execution de toutes
271 les commandes en mode commande par commande ou par lot
272 Par defaut il n'y a pas de traitement. Elle doit etre surchargee
273 pour en introduire un
275 message.info(SUPERV, "FIN D'EXECUTION %s %s", mode, etape)
277 def traiter_user_exception(self,exc_val):
278 """Cette methode realise un traitement sur les exceptions utilisateur
279 Par defaut il n'y a pas de traitement. La méthode doit etre
280 surchargée pour en introduire un.
284 def register(self,etape):
286 Cette méthode ajoute etape dans la liste des etapes : self.etapes
287 et retourne un numéro d'enregistrement
289 self.etapes.append(etape)
290 self.index_etapes[etape] = len(self.etapes) - 1
291 message.debug(SUPERV, "#%d %s", self.index_etapes[etape], etape.nom)
292 return self.g_register(etape)
294 def o_register(self,sd):
296 Retourne un identificateur pour concept
299 nom=sd.idracine + self.SEP + `self.nsd`
302 def g_register(self,etape):
304 Retourne un identificateur pour etape
306 self.nstep=self.nstep+1
307 idetape=etape.idracine + self.SEP + `self.nstep`
310 def create_sdprod(self,etape,nomsd):
312 Cette methode doit fabriquer le concept produit retourne
313 par l'etape etape et le nommer.
315 Elle est appelée à l'initiative de l'etape
316 pendant le processus de construction de cette etape :
317 methode __call__ de la classe CMD (OPER ou MACRO)
319 Ce travail est réalisé par le contexte supérieur
320 (etape.parent) car dans certains cas, le concept ne doit
321 pas etre fabriqué mais l'etape doit simplement utiliser
322 un concept préexistant.
325 - Cas 1 : etape.reuse != None : le concept est réutilisé
326 - Cas 2 : l'étape appartient à une macro qui a déclaré un
327 concept de sortie qui doit etre produit par cette
329 Dans le cas du JDC, le deuxième cas ne peut pas se produire.
331 sd= etape.get_sd_prod()
332 if sd != None and (etape.definition.reentrant == 'n' or etape.reuse is None) :
333 # ATTENTION : On ne nomme la SD que dans le cas de non reutilisation
334 # d un concept. Commande non reentrante ou reuse absent.
335 self.NommerSdprod(sd,nomsd)
338 def NommerSdprod(self,sd,sdnom,restrict='non'):
340 Nomme la SD apres avoir verifie que le nommage est possible : nom
342 Si le nom est deja utilise, leve une exception
343 Met le concept créé dans le concept global g_context
345 o=self.sds_dict.get(sdnom,None)
346 if isinstance(o,ASSD):
347 raise AsException("Nom de concept deja defini : %s" % sdnom)
348 if self._reserved_kw.get(sdnom) == 1:
349 raise AsException("Nom de concept invalide. '%s' est un mot-clé réservé." % sdnom)
351 # ATTENTION : Il ne faut pas ajouter sd dans sds car il s y trouve deja.
352 # Ajoute a la creation (appel de reg_sd).
353 self.sds_dict[sdnom]=sd
356 # En plus si restrict vaut 'non', on insere le concept dans le contexte du JDC
357 if restrict == 'non':
358 self.g_context[sdnom]=sd
359 message.debug(SUPERV, "g_context[%r] = %s", sdnom, sd)
363 Methode appelee dans l __init__ d un ASSD lors de sa creation
367 return self.o_register(sd)
369 def delete_concept_after_etape(self,etape,sd):
371 Met à jour les étapes du JDC qui sont après etape suite à
372 la disparition du concept sd
374 # Cette methode est définie dans le noyau mais ne sert que pendant
375 # la phase de creation des etapes et des concepts. Il n'y a aucun
376 # traitement particulier à réaliser.
377 # Dans d'autres conditions, il faut surcharger cette méthode
381 N_OBJECT.OBJECT.supprime(self)
382 for etape in self.etapes:
385 def get_file(self,unite=None,fic_origine=''):
387 Retourne le nom du fichier correspondant à un numero d'unité
388 logique (entier) ainsi que le source contenu dans le fichier
391 # Si le JDC est relié à une application maitre, on délègue la recherche
392 file,text= self.appli.get_file(unite,fic_origine)
396 if os.path.exists("fort."+str(unite)):
397 file= "fort."+str(unite)
399 raise AsException("Impossible de trouver le fichier correspondant"
400 " a l unite %s" % unite)
401 if not os.path.exists(file):
402 raise AsException("%s n'est pas un fichier existant" % unite)
406 if file == None : return None,None
407 text=string.replace(text,'\r\n','\n')
408 linecache.cache[file]=0,0,string.split(text,'\n'),file
411 def set_par_lot(self,par_lot):
413 Met le mode de traitement a PAR LOT
414 ou a COMMANDE par COMMANDE
415 en fonction de la valeur du mot cle PAR_LOT et
416 du contexte : application maitre ou pas
418 if self.appli == None:
419 # Pas d application maitre
422 # Avec application maitre
425 def accept(self,visitor):
427 Cette methode permet de parcourir l'arborescence des objets
428 en utilisant le pattern VISITEUR
430 visitor.visitJDC(self)
434 Cette methode a pour fonction d'ouvrir un interpreteur
435 pour que l'utilisateur entre des commandes interactivement
437 CONTEXT.set_current_step(self)
439 # Le module nommage utilise le module linecache pour accéder
440 # au source des commandes du jeu de commandes.
441 # Dans le cas d'un fichier, on accède au contenu de ce fichier
442 # Dans le cas de la console interactive, il faut pouvoir accéder
443 # aux commandes qui sont dans le buffer de la console
444 import linecache,code
445 console= code.InteractiveConsole(self.g_context,filename="<console>")
446 linecache.cache["<console>"]=0,0,console.buffer,"<console>"
447 banner="""***********************************************
448 * Interpreteur interactif %s
449 ***********************************************""" % self.code
450 console.interact(banner)
453 CONTEXT.unset_current_step()
455 def get_contexte_avant(self,etape):
457 Retourne le dictionnaire des concepts connus avant etape
458 On tient compte des commandes qui modifient le contexte
459 comme DETRUIRE ou les macros
460 Si etape == None, on retourne le contexte en fin de JDC
462 # L'étape courante pour laquelle le contexte a été calculé est
463 # mémorisée dans self.index_etape_courante
464 # XXX on pourrait faire mieux dans le cas PAR_LOT="NON" : en
466 # courante pendant le processus de construction des étapes.
467 # Si on insère des commandes (par ex, dans EFICAS), il faut préalablement
468 # remettre ce pointeur à 0
470 index_etape = self.index_etapes[etape]
472 index_etape=len(self.etapes)
473 if index_etape >= self.index_etape_courante:
474 # On calcule le contexte en partant du contexte existant
475 d=self.current_context
476 if self.index_etape_courante==0 and self.context_ini:
477 d.update(self.context_ini)
478 liste_etapes=self.etapes[self.index_etape_courante:index_etape]
480 d=self.current_context={}
482 d.update(self.context_ini)
483 liste_etapes=self.etapes
485 for e in liste_etapes:
490 self.index_etape_courante=index_etape
493 def get_global_contexte(self):
494 """Retourne "un" contexte global ;-)"""
495 # N'est utilisé que par INCLUDE (sauf erreur).
496 # g_context est remis à {} en PAR_LOT='OUI'. const_context permet
497 # de retrouver ce qui y a été mis par exec_compile.
498 # Les concepts n'y sont pas en PAR_LOT='OUI'. Ils sont ajoutés
499 # par get_global_contexte de la MACRO.
500 d = self.const_context.copy()
501 d.update(self.g_context)
505 def get_contexte_courant(self, etape_courante=None):
507 Retourne le contexte tel qu'il est (ou 'sera' si on est en phase
508 de construction) au moment de l'exécution de l'étape courante.
510 if etape_courante is None:
511 etape_courante = CONTEXT.get_current_step()
512 return self.get_contexte_avant(etape_courante)
515 def get_concept(self, nomsd):
517 Méthode pour recuperer un concept à partir de son nom
519 return self.get_contexte_courant().get(nomsd.strip(), None)
521 def del_concept(self, nomsd):
523 Méthode pour supprimer la référence d'un concept dans le sds_dict.
524 Ne détruire pas le concept (différent de supprime).
527 del self.sds_dict[nomsd.strip()]
532 def get_cmd(self,nomcmd):
534 Méthode pour recuperer la definition d'une commande
535 donnee par son nom dans les catalogues declares
538 for cata in self.cata:
539 if hasattr(cata,nomcmd):
540 return getattr(cata,nomcmd)
542 def append_reset(self,etape):
544 Ajoute une etape provenant d'un autre jdc a la liste des etapes
545 et remet à jour la parenté de l'étape et des concepts
547 self.etapes.append(etape)
548 self.index_etapes[etape] = len(self.etapes) - 1
550 etape.reset_jdc(self)
552 def sd_accessible(self):
553 """On peut acceder aux "valeurs" (jeveux) des ASSD si le JDC est en PAR_LOT="NON".
555 if CONTEXT.debug: print ' `- JDC sd_accessible : PAR_LOT =', self.par_lot
556 return self.par_lot == 'NON'
559 def _build_reserved_kw_list(self):
560 """Construit la liste des mots-clés réservés (interdits pour le
561 nommage des concepts)."""
563 for cat in self.cata:
564 wrk.update([kw for kw in dir(cat) if len(kw) <= 8 and kw == kw.upper()])
565 wrk.difference_update(['OPER', 'MACRO', 'BLOC', 'SIMP', 'FACT', 'FORM',
566 'GEOM', 'MCSIMP', 'MCFACT'])
567 self._reserved_kw = {}.fromkeys(wrk, 1)