2 Ce module contient la classe MACRO_ETAPE qui sert à vérifier et à exécuter
7 import types,sys,string
13 from N_Exception import AsException
15 from N_utils import AsType
17 class MACRO_ETAPE(N_ETAPE.ETAPE):
22 def __init__(self,oper=None,reuse=None,args={}):
26 - definition : objet portant les attributs de définition d'une étape
27 de type macro-commande. Il est initialisé par
30 - reuse : indique le concept d'entrée réutilisé. Il se trouvera donc
31 en sortie si les conditions d'exécution de l'opérateur
34 - valeur : arguments d'entrée de type mot-clé=valeur. Initialisé
42 self.parent=CONTEXT.get_current_step()
45 self.idracine=oper.label
46 self.appel=N_utils.callee_where()
50 self.current_context={}
51 self.index_etape_courante=0
54 # Dans le cas d'une macro écrite en Python, l'attribut Outputs est un
55 # dictionnaire qui contient les concepts produits de sortie
56 # (nom : ASSD) déclarés dans la fonction sd_prod
63 def make_register(self):
65 Initialise les attributs jdc, id, niveau et réalise les enregistrements
69 self.jdc = self.parent.get_jdc_root()
70 self.id=self.parent.register(self)
73 self.jdc = self.parent =None
77 def Build_sd(self,nom):
79 Construit le concept produit de l'opérateur. Deux cas
80 peuvent se présenter :
82 - le parent n'est pas défini. Dans ce cas, l'étape prend en charge
83 la création et le nommage du concept.
85 - le parent est défini. Dans ce cas, l'étape demande au parent la
86 création et le nommage du concept.
89 if not self.isactif():return
91 # On positionne la macro self en tant que current_step pour que les
92 # étapes créées lors de l'appel à sd_prod et à op_init aient la macro
94 self.set_current_step()
96 sd= self.parent.create_sdprod(self,nom)
97 if type(self.definition.op_init) == types.FunctionType:
98 apply(self.definition.op_init,(self,self.parent.g_context))
100 sd=self.get_sd_prod()
101 if sd != None and self.reuse == None:
102 # On ne nomme le concept que dans le cas de non reutilisation
105 self.reset_current_step()
106 if self.jdc and self.jdc.par_lot == "NON" :
109 except AsException,e:
110 self.reset_current_step()
111 raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
112 'fichier : ',self.appel[1],e)
114 #self.reset_current_step()
117 self.reset_current_step()
118 l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
119 raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
120 'fichier : ',self.appel[1]+'\n',
123 def get_sd_prod(self):
125 Retourne le concept résultat d'une macro étape
126 La difference avec une etape ou une proc-etape tient a ce que
127 le concept produit peut exister ou pas
128 Si sd_prod == None le concept produit n existe pas on retourne None
130 cas 1 : sd_prod n'est pas une fonction
131 il s'agit d'une sous classe de ASSD
132 on construit le sd à partir de cette classe
134 cas 2 : sd_prod est une fonction
135 on l'évalue avec les mots-clés de l'étape (mc_liste)
136 on construit le sd à partir de la classe obtenue
139 sd_prod=self.definition.sd_prod
141 if type(self.definition.sd_prod) == types.FunctionType:
142 d=self.cree_dict_valeurs(self.mc_liste)
144 # la sd_prod d'une macro a l'objet macro_etape lui meme en premier argument
145 # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
146 # les concepts produits dans self.sdprods, il faut le mettre à zéro avant de l'appeler
148 sd_prod= apply(sd_prod,(self,),d)
152 if CONTEXT.debug: traceback.print_exc()
153 l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
154 raise AsException("impossible d affecter un type au resultat\n",string.join(l[2:]))
156 # on teste maintenant si la SD est réutilisée ou s'il faut la créer
158 if AsType(self.reuse) != sd_prod:
159 raise AsException("type de concept reutilise incompatible avec type produit")
165 self.sd= sd_prod(etape=self)
167 if self.definition.reentrant == 'o':
171 def get_type_produit(self,force=0):
173 Retourne le type du concept résultat de l'étape et eventuellement type
174 les concepts produits "à droite" du signe égal (en entrée)
176 cas 1 : sd_prod de oper n'est pas une fonction
177 il s'agit d'une sous classe de ASSD
178 on retourne le nom de la classe
179 cas 2 : il s'agit d'une fonction
180 on l'évalue avec les mots-clés de l'étape (mc_liste)
181 et on retourne son résultat
183 if not force and hasattr(self,'typret'): return self.typret
184 if type(self.definition.sd_prod) == types.FunctionType:
185 d=self.cree_dict_valeurs(self.mc_liste)
187 # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
188 # les concepts produits dans self.sdprods, il faut le mettre à zéro
190 sd_prod= apply(self.definition.sd_prod,(self,),d)
192 #traceback.print_exc()
195 sd_prod=self.definition.sd_prod
198 def get_contexte_avant(self,etape):
200 Retourne le dictionnaire des concepts connus avant etape
201 pour les commandes internes a la macro
202 On tient compte des commandes qui modifient le contexte
203 comme DETRUIRE ou les macros
205 # L'étape courante pour laquelle le contexte a été calculé est
206 # mémorisée dans self.index_etape_courante
207 # Si on insère des commandes (par ex, dans EFICAS), il faut
208 # préalablement remettre ce pointeur à 0
209 index_etape=self.etapes.index(etape)
210 if index_etape >= self.index_etape_courante:
211 # On calcule le contexte en partant du contexte existant
212 d=self.current_context
213 liste_etapes=self.etapes[self.index_etape_courante:index_etape]
215 d=self.current_context={}
216 liste_etapes=self.etapes
218 for e in liste_etapes:
223 self.index_etape_courante=index_etape
228 Méthode qui supprime toutes les références arrières afin que
229 l'objet puisse être correctement détruit par le garbage collector
231 N_MCCOMPO.MCCOMPO.supprime(self)
234 if self.sd : self.sd.supprime()
235 for concept in self.sdprods:
237 for etape in self.etapes:
240 def type_sdprod(self,co,t):
242 Cette methode a pour fonction de typer le concept co avec le type t
243 dans les conditions suivantes
244 1- co est un concept produit de self
245 2- co est un concept libre : on le type et on l attribue à self
246 Elle enregistre egalement les concepts produits (on fait l hypothese
247 que la liste sdprods a été correctement initialisee, vide probablement)
249 if not hasattr(co,'etape'):
250 # Le concept vaut None probablement. On ignore l'appel
254 # le concept est libre
257 self.sdprods.append(co)
258 elif co.etape== self:
259 # le concept est produit par self
261 self.sdprods.append(co)
262 elif co.etape== self.parent:
263 # le concept est produit par la macro superieure
264 # on transfere la propriete
265 # On verifie que le type du concept existant co.__class__ est un sur type de celui attendu
266 # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
267 if not issubclass(t,co.__class__):
268 raise AsException("Le type du concept produit %s devrait etre une sur classe de %s" %(co.__class__,t))
271 self.sdprods.append(co)
272 elif self.issubstep(co.etape):
273 # Le concept est propriété d'une sous etape de self. Il doit etre considere
274 # comme produit par la macro => ajout dans self.sdprods
275 self.sdprods.append(co)
277 # le concept est produit par une autre étape
280 def issubstep(self,etape):
282 Cette methode retourne un entier indiquant si etape est une
283 sous etape de la macro self ou non
287 if etape in self.etapes:return 1
288 for etap in self.etapes:
289 if etap.issubstep(etape):return 1
292 def register(self,etape):
294 Enregistrement de etape dans le contexte de la macro : liste etapes
295 et demande d enregistrement global aupres du JDC
297 self.etapes.append(etape)
298 idetape=self.jdc.g_register(etape)
303 Methode appelee dans l __init__ d un ASSD a sa creation pour
304 s enregistrer (reserve aux ASSD créés au sein d'une MACRO)
307 return self.jdc.o_register(sd)
309 def create_sdprod(self,etape,nomsd):
311 Intention : Cette methode doit fabriquer le concept produit retourne
312 par l'etape etape et le nommer.
313 Elle est appelée à l'initiative de l'etape
314 pendant le processus de construction de cette etape : methode __call__
315 de la classe CMD (OPER ou MACRO)
316 Ce travail est réalisé par le contexte supérieur (etape.parent)
317 car dans certains cas, le concept ne doit pas etre fabriqué mais
318 l'etape doit simplement utiliser un concept préexistant.
319 Cas 1 : etape.reuse != None : le concept est réutilisé
320 Cas 2 : l'étape appartient à une macro qui a déclaré un concept
321 de sortie qui doit etre produit par cette etape.
323 if self.Outputs.has_key(nomsd):
324 # Il s'agit d'un concept de sortie de la macro. Il ne faut pas le créer
325 # Il faut quand meme appeler la fonction sd_prod si elle existe.
326 # get_type_produit le fait et donne le type attendu par la commande pour verification ultérieure.
327 sdprod=etape.get_type_produit()
328 sd=self.Outputs[nomsd]
329 # On verifie que le type du concept existant sd.__class__ est un sur type de celui attendu
330 # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
331 if not issubclass(sdprod,sd.__class__):
332 raise AsException("Le type du concept produit %s devrait etre une sur classe de %s" %(sd.__class__,sdprod))
333 # La propriete du concept est transferee a l'etape avec le type attendu par l'étape
336 #XXX Il semble plus logique que ce soit class et non pas call ???
340 sd= etape.get_sd_prod()
341 if sd != None and etape.reuse == None:
342 # ATTENTION : On ne nomme la SD que dans le cas de non reutilisation d un concept
343 self.NommerSdprod(sd,nomsd)
346 def NommerSdprod(self,sd,sdnom):
348 Cette methode est appelee par les etapes internes de la macro
349 La macro appelle le JDC pour valider le nommage
350 On considere que l espace de nom est unique et géré par le JDC
351 Si le nom est deja utilise, l appel leve une exception
353 #XXX attention inconsistence : prefix et gcncon ne sont pas
354 # définis dans le package Noyau. La methode NommerSdprod pour
355 # les macros devrait peut etre etre déplacée dans Build ???
356 if CONTEXT.debug : print "MACRO.NommerSdprod: ",sd,sdnom
357 if hasattr(self,'prefix'):
358 # Dans le cas de l'include_materiau on ajoute un prefixe au nom du concept
359 if sdnom != self.prefix:sdnom=self.prefix+sdnom
360 if self.Outputs.has_key(sdnom):
361 # Il s'agit d'un concept de sortie de la macro produit par une sous commande
362 sdnom=self.Outputs[sdnom].nom
363 elif sdnom[0] == '_':
364 # Si le nom du concept commence par le caractere _ on lui attribue
365 # un identificateur JEVEUX construit par gcncon et respectant
366 # la regle gcncon legerement adaptee ici
367 # nom commencant par __ : il s'agit de concepts qui seront detruits
368 # nom commencant par _ : il s'agit de concepts intermediaires qui seront gardes
369 # ATTENTION : il faut traiter différemment les concepts dont le nom
370 # commence par _ mais qui sont des concepts nommés automatiquement par
371 # une éventuelle sous macro.
372 # Le test suivant n'est pas tres rigoureux mais permet de fonctionner pour le moment (a améliorer)
373 if sdnom[1] in string.digits:
374 # Ce concept provient probablement d'une macro appelee par self
376 elif sdnom[1] == '_':
377 sdnom=self.gcncon('.')
379 sdnom=self.gcncon('_')
380 if self.sd != None and self.sd.nom == sdnom :
381 # Il s'agit du concept produit par la macro, il a deja ete nomme.
382 # On se contente de donner le meme nom au concept produit par la sous commande
383 # sans passer par la routine de nommage
386 # On propage le nommage au contexte superieur
387 self.parent.NommerSdprod(sd,sdnom)
389 def delete_concept_after_etape(self,etape,sd):
391 Met à jour les étapes de la MACRO qui sont après etape suite à
392 la disparition du concept sd
394 # Cette methode est définie dans le noyau mais ne sert que pendant la phase de creation
395 # des etapes et des concepts. Il n'y a aucun traitement particulier à réaliser
396 # Dans d'autres conditions, il faudrait surcharger cette méthode.
399 def accept(self,visitor):
401 Cette methode permet de parcourir l'arborescence des objets
402 en utilisant le pattern VISITEUR
404 visitor.visitMACRO_ETAPE(self)
406 def update_context(self,d):
408 Met à jour le contexte contenu dans le dictionnaire d
409 Une MACRO_ETAPE peut ajouter plusieurs concepts dans le contexte
410 Une fonction enregistree dans op_init peut egalement modifier le contexte
412 if type(self.definition.op_init) == types.FunctionType:
413 apply(self.definition.op_init,(self,d))
414 if self.sd != None:d[self.sd.nom]=self.sd
415 for co in self.sdprods:
418 def make_include(self,unite=None):
420 Inclut un fichier dont l'unite logique est unite
422 if not unite : return
423 f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
424 self.fichier_init = f
426 self.make_contexte(f,text)
428 def make_poursuite(self):
430 Inclut un fichier poursuite
432 f,text=self.get_file(fic_origine=self.parent.nom)
435 self.make_contexte(f,text)
437 def make_contexte(self,f,text):
439 Interprete le texte fourni (text) issu du fichier f
440 dans le contexte du parent.
441 Cette methode est utile pour le fonctionnement des
444 # on execute le texte fourni dans le contexte forme par
445 # le contexte de l etape pere (global au sens Python)
446 # et le contexte de l etape (local au sens Python)
447 code=compile(text,f,'exec')
450 self.contexte_fichier_init = d
451 exec code in self.parent.g_context,d