Salome HOME
Modif V6_4_°
[tools/eficas.git] / Noyau / N_MACRO_ETAPE.py
1 #@ MODIF N_MACRO_ETAPE Noyau  DATE 07/11/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.
11 #
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.
16 #
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.
20 #
21 #
22 # ======================================================================
23
24
25 """
26     Ce module contient la classe MACRO_ETAPE qui sert à vérifier et à exécuter
27     une commande
28 """
29
30 # Modules Python
31 import types,sys,string
32 import traceback
33
34 # Modules EFICAS
35 import N_MCCOMPO
36 import N_ETAPE
37 from N_Exception import AsException
38 import N_utils
39 from N_utils import AsType
40 from N_CO import CO
41 from N_ASSD import ASSD
42 from N_info import message, SUPERV
43
44 class MACRO_ETAPE(N_ETAPE.ETAPE):
45    """
46
47    """
48    nature = "COMMANDE"
49    typeCO=CO
50    def __init__(self,oper=None,reuse=None,args={}):
51       """
52          Attributs :
53
54             - definition : objet portant les attributs de définition d'une étape
55               de type macro-commande. Il est initialisé par
56               l'argument oper.
57
58             - reuse : indique le concept d'entrée réutilisé. Il se trouvera donc
59               en sortie si les conditions d'exécution de l'opérateur
60               l'autorise
61
62             - valeur : arguments d'entrée de type mot-clé=valeur. Initialisé
63               avec l'argument args.
64
65       """
66       self.definition = oper
67       self.reuse = reuse
68       self.valeur = args
69       self.nettoiargs()
70       self.parent = CONTEXT.get_current_step()
71       self.etape = self
72       self.nom = oper.nom
73       self.idracine = oper.label
74       self.appel = N_utils.callee_where()
75       self.mc_globaux = {}
76       self.g_context = {}
77       # Contexte courant
78       self.current_context = {}
79       self.macro_const_context = {}
80       self.index_etape_courante = 0
81       self.etapes = []
82       self.index_etapes = {}
83       self.sds = []
84       #  Dans le cas d'une macro écrite en Python, l'attribut Outputs est un
85       #  dictionnaire qui contient les concepts produits de sortie
86       #  (nom : ASSD) déclarés dans la fonction sd_prod
87       self.Outputs = {}
88       self.sd = None
89       self.actif = 1
90       self.sdprods = []
91       self.make_register()
92       self.UserError = "UserError"
93
94    def make_register(self):
95       """
96          Initialise les attributs jdc, id, niveau et réalise les enregistrements
97          nécessaires
98       """
99       if self.parent :
100          self.jdc = self.parent.get_jdc_root()
101          self.id=self.parent.register(self)
102          self.niveau=None
103          self.UserError=self.jdc.UserError
104       else:
105          self.jdc = self.parent =None
106          self.id=None
107          self.niveau=None
108          self.UserError="UserError"
109
110    def Build_sd(self,nom):
111       """
112          Construit le concept produit de l'opérateur. Deux cas
113          peuvent se présenter :
114
115            - le parent n'est pas défini. Dans ce cas, l'étape prend en charge
116              la création et le nommage du concept.
117
118            - le parent est défini. Dans ce cas, l'étape demande au parent la
119              création et le nommage du concept.
120
121       """
122       message.debug(SUPERV, "Build_sd %s", self.nom)
123       self.sdnom=nom
124       try:
125          # On positionne la macro self en tant que current_step pour que les
126          # étapes créées lors de l'appel à sd_prod et à op_init aient la macro
127          #  comme parent
128          self.set_current_step()
129          if self.parent:
130             sd= self.parent.create_sdprod(self,nom)
131             if type(self.definition.op_init) == types.FunctionType:
132                apply(self.definition.op_init,(self,self.parent.g_context))
133          else:
134             sd=self.get_sd_prod()
135             if sd != None and self.reuse == None:
136                # On ne nomme le concept que dans le cas de non reutilisation
137                # d un concept
138                sd.set_name(nom)
139          self.reset_current_step()
140       except AsException,e:
141          self.reset_current_step()
142          raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
143                               'fichier : ',self.appel[1],e)
144       except (EOFError, self.UserError):
145          # Le retablissement du step courant n'est pas strictement necessaire. On le fait pour des raisons de coherence
146          self.reset_current_step()
147          raise
148       except :
149          self.reset_current_step()
150          l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
151          raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
152                            'fichier : ',self.appel[1]+'\n',
153                             string.join(l))
154
155       self.Execute()
156       return sd
157
158    def mark_CO(self):
159       """
160          Marquage des concepts CO d'une macro-commande
161       """
162       # On marque les concepts CO pour verification ulterieure de leur bonne utilisation
163       l=self.get_all_co()
164       for c in l:
165           #if not hasattr(c,"_etape") or c._etape is not c.etape:
166              c._etape=self
167       return l
168
169    def get_sd_prod(self):
170       """
171         Retourne le concept résultat d'une macro étape
172         La difference avec une etape ou une proc-etape tient a ce que
173         le concept produit peut exister ou pas
174
175         Si sd_prod == None le concept produit n existe pas on retourne None
176
177         Deux cas :
178          - cas 1 : sd_prod  n'est pas une fonction
179                  il s'agit d'une sous classe de ASSD
180                  on construit le sd à partir de cette classe
181                  et on le retourne
182          - cas 2 : sd_prod est une fonction
183                  on l'évalue avec les mots-clés de l'étape (mc_liste)
184                  on construit le sd à partir de la classe obtenue
185                  et on le retourne
186       """
187       sd_prod=self.definition.sd_prod
188       self.typret=None
189       # On marque les concepts CO pour verification ulterieure de leur bonne utilisation
190       self.mark_CO()
191
192       if type(self.definition.sd_prod) == types.FunctionType:
193         d=self.cree_dict_valeurs(self.mc_liste)
194         try:
195           # la sd_prod d'une macro a l'objet macro_etape lui meme en premier argument
196           # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
197           # les concepts produits dans self.sdprods, il faut le mettre à zéro avant de l'appeler
198           self.sdprods=[]
199           sd_prod= apply(sd_prod,(self,),d)
200         except (EOFError, self.UserError), exc:
201           raise
202         except:
203           if CONTEXT.debug: traceback.print_exc()
204           l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
205           raise AsException("impossible d affecter un type au resultat\n",string.join(l[2:]))
206
207       # on teste maintenant si la SD est réutilisée ou s'il faut la créer
208       if self.definition.reentrant != 'n' and self.reuse:
209         # Le concept produit est specifie reutilise (reuse=xxx). C'est une erreur mais non fatale.
210         # Elle sera traitee ulterieurement.
211         self.sd=self.reuse
212       else:
213         if sd_prod == None:
214           self.sd=None
215         else:
216           self.sd= sd_prod(etape=self)
217           self.typret=sd_prod
218           # Si la commande est obligatoirement reentrante et reuse n'a pas ete specifie, c'est une erreur.
219           # On ne fait rien ici. L'erreur sera traitee par la suite.
220       # précaution
221       if self.sd is not None and not isinstance(self.sd, ASSD):
222          raise AsException("""
223 Impossible de typer le résultat !
224 Causes possibles :
225    Utilisateur : Soit la valeur fournie derrière "reuse" est incorrecte,
226                  soit il y a une "," à la fin d'une commande précédente.
227    Développeur : La fonction "sd_prod" retourne un type invalide.""")
228       return self.sd
229
230    def get_type_produit(self,force=0):
231       try:
232           return self.get_type_produit_brut(force)
233       except:
234           #traceback.print_exc()
235           return None
236
237    def get_type_produit_brut(self,force=0):
238       """
239            Retourne le type du concept résultat de l'étape et eventuellement type
240            les concepts produits "à droite" du signe égal (en entrée)
241
242            Deux cas :
243              - cas 1 : sd_prod de oper n'est pas une fonction
244                     il s'agit d'une sous classe de ASSD
245                     on retourne le nom de la classe
246              - cas 2 : il s'agit d'une fonction
247                     on l'évalue avec les mots-clés de l'étape (mc_liste)
248                     et on retourne son résultat
249       """
250       if not force and hasattr(self,'typret'): return self.typret
251       # On marque les concepts CO pour verification ulterieure de leur bonne utilisation
252       self.mark_CO()
253
254       if type(self.definition.sd_prod) == types.FunctionType:
255         d=self.cree_dict_valeurs(self.mc_liste)
256         # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
257         # les concepts produits dans self.sdprods, il faut le mettre à zéro
258         self.sdprods=[]
259         sd_prod= apply(self.definition.sd_prod,(self,),d)
260       else:
261         sd_prod=self.definition.sd_prod
262       return sd_prod
263
264    def get_contexte_avant(self,etape):
265       """
266           Retourne le dictionnaire des concepts connus avant etape
267           pour les commandes internes a la macro
268           On tient compte des commandes qui modifient le contexte
269           comme DETRUIRE ou les macros
270       """
271       # L'étape courante pour laquelle le contexte a été calculé est
272       # mémorisée dans self.index_etape_courante
273       # Si on insère des commandes (par ex, dans EFICAS), il faut
274       # préalablement remettre ce pointeur à 0
275       if etape:
276          index_etape = self.index_etapes[etape]
277       else:
278          index_etape=len(self.etapes)
279
280       if index_etape >= self.index_etape_courante:
281          # On calcule le contexte en partant du contexte existant
282          d=self.current_context
283          liste_etapes=self.etapes[self.index_etape_courante:index_etape]
284       else:
285          d=self.current_context={}
286          liste_etapes=self.etapes
287
288       for e in liste_etapes:
289         if e is etape:
290            break
291         if e.isactif():
292            e.update_context(d)
293       self.index_etape_courante=index_etape
294       message.debug(SUPERV, "returns %s", d.keys())
295       return d
296
297    def supprime(self):
298       """
299          Méthode qui supprime toutes les références arrières afin que
300          l'objet puisse etre correctement détruit par le garbage collector
301       """
302       N_MCCOMPO.MCCOMPO.supprime(self)
303       self.jdc=None
304       self.appel=None
305       if self.sd : self.sd.supprime()
306       for concept in self.sdprods:
307          concept.supprime()
308       for etape in self.etapes:
309          etape.supprime()
310
311    def type_sdprod(self,co,t):
312       """
313            Cette methode a pour fonction de typer le concept co avec le type t
314            dans les conditions suivantes :
315             1. co est un concept produit de self
316             2. co est un concept libre : on le type et on l attribue à self
317
318            Elle enregistre egalement les concepts produits (on fait l hypothese
319            que la liste sdprods a été correctement initialisee, vide probablement)
320       """
321       if not hasattr(co,'etape'):
322          # Le concept vaut None probablement. On ignore l'appel
323          return
324       #
325       # On cherche a discriminer les differents cas de typage d'un concept
326       # produit par une macro qui est specifie dans un mot cle simple.
327       # On peut passer plusieurs fois par type_sdprod ce qui explique
328       # le nombre important de cas.
329       #
330       # Cas 1 : Le concept est libre. Il vient d'etre cree par CO(nom)
331       # Cas 2 : Le concept est produit par la macro. On est deja passe par type_sdprod.
332       #         Cas semblable a Cas 1.
333       # Cas 3 : Le concept est produit par la macro englobante (parent). On transfere
334       #         la propriete du concept de la macro parent a la macro courante (self)
335       #         en verifiant que le type est valide
336       # Cas 4 : La concept est la propriete d'une etape fille. Ceci veut dire qu'on est
337       #         deja passe par type_sdprod et que la propriete a ete transfere a une
338       #         etape fille. Cas semblable a Cas 3.
339       # Cas 5 : Le concept est produit par une etape externe a la macro.
340       #
341       if co.etape == None:
342          # Cas 1 : le concept est libre
343          # On l'attache a la macro et on change son type dans le type demande
344          # Recherche du mot cle simple associe au concept
345          mcs=self.get_mcs_with_co(co)
346          if len(mcs) != 1:
347             raise AsException("""Erreur interne.
348 Il ne devrait y avoir qu'un seul mot cle porteur du concept CO (%s)""" % co)
349          mcs=mcs[0]
350          if not self.typeCO in mcs.definition.type:
351             raise AsException("""Erreur interne.
352 Impossible de changer le type du concept (%s). Le mot cle associe ne supporte pas CO mais seulement (%s)""" %(co,mcs.definition.type))
353          co.etape = self
354          # affectation du bon type du concept et
355          co.change_type(t)
356          self.sdprods.append(co)
357
358       elif co.etape == self:
359          # Cas 2 : le concept est produit par la macro (self)
360          # On est deja passe par type_sdprod (Cas 1 ou 3).
361          if co.etape == co._etape:
362            #Le concept a été créé par la macro (self)
363            #On peut changer son type
364            co.change_type(t)
365          else:
366            #Le concept a été créé par une macro parente
367            # Le type du concept doit etre coherent avec le type demande (seulement derive)
368            if not isinstance(co,t):
369              raise AsException("""Erreur interne.
370 Le type demande (%s) et le type du concept (%s) devraient etre derives""" %(t,co.__class__))
371
372          self.sdprods.append(co)
373
374       elif co.etape== self.parent:
375          # Cas 3 : le concept est produit par la macro parente (self.parent)
376          # on transfere la propriete du concept a la macro fille
377          # et on change le type du concept comme demande
378          # Au prealable, on verifie que le concept existant (co) est une instance
379          # possible du type demande (t)
380          # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
381          if not isinstance(co,t):
382             raise AsException("""
383 Impossible de changer le type du concept produit (%s) en (%s).
384 Le type actuel (%s) devrait etre une classe derivee du nouveau type (%s)""" % (co,t,co.__class__,t))
385          mcs=self.get_mcs_with_co(co)
386          if len(mcs) != 1:
387             raise AsException("""Erreur interne.
388 Il ne devrait y avoir qu'un seul mot cle porteur du concept CO (%s)""" % co)
389          mcs=mcs[0]
390          if not self.typeCO in mcs.definition.type:
391             raise AsException("""Erreur interne.
392 Impossible de changer le type du concept (%s). Le mot cle associe ne supporte pas CO mais seulement (%s)""" %(co,mcs.definition.type))
393          co.etape=self
394          # On ne change pas le type car il respecte la condition isinstance(co,t)
395          #co.__class__ = t
396          self.sdprods.append(co)
397
398       elif self.issubstep(co.etape):
399          # Cas 4 : Le concept est propriété d'une sous etape de la macro (self).
400          # On est deja passe par type_sdprod (Cas 3 ou 1).
401          # Il suffit de le mettre dans la liste des concepts produits (self.sdprods)
402          # Le type du concept et t doivent etre derives.
403          # Il n'y a aucune raison pour que la condition ne soit pas verifiee.
404          if not isinstance(co,t):
405             raise AsException("""Erreur interne.
406 Le type demande (%s) et le type du concept (%s) devraient etre derives""" %(t,co.__class__))
407          self.sdprods.append(co)
408
409       else:
410          # Cas 5 : le concept est produit par une autre étape
411          # On ne fait rien
412          return
413
414    def issubstep(self,etape):
415       """
416           Cette methode retourne un entier indiquant si etape est une
417           sous etape de la macro self ou non
418           1 = oui
419           0 = non
420       """
421       if etape in self.etapes:return 1
422       for etap in self.etapes:
423         if etap.issubstep(etape):return 1
424       return 0
425
426    def register(self,etape):
427       """
428           Enregistrement de etape dans le contexte de la macro : liste etapes
429           et demande d enregistrement global aupres du JDC
430       """
431       self.etapes.append(etape)
432       self.index_etapes[etape] = len(self.etapes) - 1
433       idetape=self.jdc.g_register(etape)
434       return idetape
435
436    def reg_sd(self,sd):
437       """
438            Methode appelee dans l __init__ d un ASSD a sa creation pour
439            s enregistrer (reserve aux ASSD créés au sein d'une MACRO)
440       """
441       self.sds.append(sd)
442       return self.jdc.o_register(sd)
443
444    def create_sdprod(self,etape,nomsd):
445       """
446           Cette methode doit fabriquer le concept produit retourne
447           par l'etape etape et le nommer.
448
449           Elle est appelée à l'initiative de l'etape
450           pendant le processus de construction de cette etape : methode __call__
451           de la classe CMD (OPER ou MACRO)
452           Ce travail est réalisé par le contexte supérieur (etape.parent)
453           car dans certains cas, le concept ne doit pas etre fabriqué mais
454           l'etape doit simplement utiliser un concept préexistant.
455                   - Cas 1 : etape.reuse != None : le concept est réutilisé
456                   - Cas 2 : l'étape appartient à une macro qui a déclaré un concept
457                     de sortie qui doit etre produit par cette etape.
458       """
459       if self.Outputs.has_key(nomsd):
460          # Il s'agit d'un concept de sortie de la macro. Il ne faut pas le créer
461          # Il faut quand meme appeler la fonction sd_prod si elle existe.
462          # get_type_produit le fait et donne le type attendu par la commande pour verification ultérieure.
463          sdprod=etape.get_type_produit_brut()
464          sd=self.Outputs[nomsd]
465          # On verifie que le type du concept existant sd.__class__ est un sur type de celui attendu
466          # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
467          if not issubclass(sdprod,sd.__class__):
468             raise AsException("Le type du concept produit %s devrait etre une sur classe de %s" %(sd.__class__,sdprod))
469          # La propriete du concept est transferee a l'etape avec le type attendu par l'étape
470          etape.sd=sd
471          sd.etape=etape
472          if self.reuse == sd and etape.reuse != sd \
473                 and getattr(sd, "executed", 0) == 1: # n'a pas été pas détruit
474             raise AsException("Le concept '%s' est réentrant dans la macro-commande %s. " \
475                               "Il devrait donc l'être dans %s (produit sous le nom '%s')." \
476                                 % (sd.nom, self.nom, etape.nom, nomsd))
477          # On donne au concept le type produit par la sous commande.
478          # Le principe est le suivant : apres avoir verifie que le type deduit par la sous commande
479          # est bien coherent avec celui initialement affecte par la macro (voir ci dessus)
480          # on affecte au concept ce type car il peut etre plus precis (derive, en general)
481          sd.__class__=sdprod
482          # On force également le nom stocké dans l'attribut sdnom : on lui donne le nom
483          # du concept associé à nomsd
484          etape.sdnom=sd.nom
485       elif etape.definition.reentrant != 'n' and etape.reuse != None:
486          # On est dans le cas d'une commande avec reutilisation d'un concept existant
487          # get_sd_prod fait le necessaire : verifications, associations, etc. mais ne cree
488          # pas un nouveau concept. Il retourne le concept reutilise
489          sd= etape.get_sd_prod()
490          # Dans le cas d'un concept nomme automatiquement : _xxx, __xxx,
491          # On force le nom stocke dans l'attribut sdnom  de l'objet etape : on lui donne le nom
492          # du concept  reutilise (sd ou etape.reuse c'est pareil)
493          # Ceci est indispensable pour eviter des erreurs lors des verifications des macros
494          # En effet une commande avec reutilisation d'un concept verifie que le nom de
495          # la variable a gauche du signe = est le meme que celui du concept reutilise.
496          # Lorsqu'une telle commande apparait dans une macro, on supprime cette verification.
497          if (etape.sdnom == '' or etape.sdnom[0] == '_'):
498             etape.sdnom=sd.nom
499       else:
500          # On est dans le cas de la creation d'un nouveau concept
501          sd= etape.get_sd_prod()
502          if sd != None :
503             self.NommerSdprod(sd,nomsd)
504       return sd
505
506    def NommerSdprod(self,sd,sdnom,restrict='non'):
507       """
508           Cette methode est appelee par les etapes internes de la macro
509           La macro appelle le JDC pour valider le nommage
510           On considere que l espace de nom est unique et géré par le JDC
511           Si le nom est deja utilise, l appel leve une exception
512           Si restrict=='non', on insere le concept dans le contexte de la macro
513           Si restrict=='oui', on n'insere pas le concept dans le contexte de la macro
514       """
515       # Normalement, lorsqu'on appelle cette methode, on ne veut nommer que des concepts nouvellement crees.
516       # Le filtrage sur les concepts a creer ou a ne pas creer est fait dans la methode
517       # create_sdprod. La seule chose a verifier apres conversion eventuelle du nom
518       # est de verifier que le nom n'est pas deja attribue. Ceci est fait en delegant
519       # au JDC par l'intermediaire du parent.
520
521       #XXX attention inconsistence : gcncon n'est pas
522       # défini dans le package Noyau. La methode NommerSdprod pour
523       # les macros devrait peut etre etre déplacée dans Build ???
524       if self.Outputs.has_key(sdnom):
525         # Il s'agit d'un concept de sortie de la macro produit par une sous commande
526         sdnom=self.Outputs[sdnom].nom
527       elif sdnom != '' and sdnom[0] == '_':
528         # Si le nom du concept commence par le caractere _ on lui attribue
529         # un identificateur JEVEUX construit par gcncon et respectant
530         # la regle gcncon legerement adaptee ici
531         # nom commencant par __ : il s'agit de concepts qui seront detruits
532         # nom commencant par _ : il s'agit de concepts intermediaires qui seront gardes
533         # ATTENTION : il faut traiter différemment les concepts dont le nom
534         # commence par _ mais qui sont des concepts nommés automatiquement par
535         # une éventuelle sous macro.
536         # Le test suivant n'est pas tres rigoureux mais permet de fonctionner pour le moment (a améliorer)
537         if sdnom[1] in string.digits:
538           # Ce concept provient probablement d'une macro appelee par self
539           pass
540         elif sdnom[1] == '_':
541           sdnom=self.gcncon('.')
542         else:
543           sdnom=self.gcncon('_')
544       else:
545         # On est dans le cas d'un nom de concept global.
546         pass
547
548       if restrict == 'non':
549          # On demande le nommage au parent mais sans ajout du concept dans le contexte du parent
550          # car on va l'ajouter dans le contexte de la macro
551          self.parent.NommerSdprod(sd,sdnom,restrict='oui')
552          # On ajoute dans le contexte de la macro les concepts nommes
553          # Ceci est indispensable pour les CO (macro) dans un INCLUDE
554          self.g_context[sdnom]=sd
555          message.debug(SUPERV, "g_context[%s] = %s", sdnom, sd)
556       else:
557          # La demande de nommage vient probablement d'une macro qui a mis
558          # le concept dans son contexte. On ne traite plus que le nommage (restrict="oui")
559          self.parent.NommerSdprod(sd,sdnom,restrict='oui')
560
561    def delete_concept_after_etape(self,etape,sd):
562       """
563           Met à jour les étapes de la MACRO  qui sont après etape suite à
564           la disparition du concept sd
565       """
566       # Cette methode est définie dans le noyau mais ne sert que pendant la phase de creation
567       # des etapes et des concepts. Il n'y a aucun traitement particulier à réaliser
568       # Dans d'autres conditions, il faudrait surcharger cette méthode.
569       return
570
571    def accept(self,visitor):
572       """
573          Cette methode permet de parcourir l'arborescence des objets
574          en utilisant le pattern VISITEUR
575       """
576       visitor.visitMACRO_ETAPE(self)
577
578    def update_context(self,d):
579       """
580          Met à jour le contexte contenu dans le dictionnaire d
581          Une MACRO_ETAPE peut ajouter plusieurs concepts dans le contexte
582          Une fonction enregistree dans op_init peut egalement modifier le contexte
583       """
584       if type(self.definition.op_init) == types.FunctionType:
585         apply(self.definition.op_init,(self,d))
586       if self.sd != None:d[self.sd.nom]=self.sd
587       for co in self.sdprods:
588         d[co.nom]=co
589
590    def make_include(self,unite=None):
591       """
592           Inclut un fichier dont l'unite logique est unite
593       """
594       if not unite : return
595       f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
596       self.fichier_init = f
597       if f == None:return
598       self.make_contexte(f,text)
599
600    def make_poursuite(self):
601       """
602           Inclut un fichier poursuite
603       """
604       try:
605          f,text=self.get_file(fic_origine=self.parent.nom)
606       except:
607          raise AsException("Impossible d'ouvrir la base pour une poursuite")
608       self.fichier_init=f
609       if f == None:return
610       self.make_contexte(f,text)
611
612    def make_contexte(self,f,text):
613       """
614           Interprete le texte fourni (text) issu du fichier f
615           dans le contexte du parent.
616           Cette methode est utile pour le fonctionnement des
617           INCLUDE
618       """
619       # on execute le texte fourni dans le contexte forme par
620       # le contexte de l etape pere (global au sens Python)
621       # et le contexte de l etape (local au sens Python)
622       code = compile(text,f,'exec')
623       d = self.macro_const_context
624       self.g_context = d
625       self.contexte_fichier_init = d
626       globs = self.get_global_contexte()
627       exec code in globs, d
628       # pour ne pas conserver des références sur tout
629       self.macro_const_context = {}
630
631    def get_global_contexte(self):
632       """
633           Cette methode retourne le contexte global fourni
634           par le parent(self) a une etape fille (l'appelant) pour
635           realiser des evaluations de texte Python (INCLUDE,...)
636       """
637       # Le contexte global est forme par concatenation du contexte
638       # du parent de self et de celui de l'etape elle meme (self)
639       d=self.parent.get_global_contexte()
640       d.update(self.g_context)
641       # en PAR_LOT='OUI', les concepts n'étant pas dans jdc.g_context,
642       # on demande au parent le contexte courant.
643       d.update(self.parent.get_contexte_avant(self))
644       return d
645
646    def get_contexte_courant(self, etape_fille_du_jdc=None):
647       """
648          Retourne le contexte tel qu'il est au moment de l'exécution de
649          l'étape courante.
650       """
651       ctx = {}
652       # update car par ricochet on modifierait jdc.current_context
653       ctx.update( self.parent.get_contexte_courant(self) )
654       # on peut mettre None car toujours en PAR_LOT='NON', donc la dernière
655       ctx.update( self.get_contexte_avant(None) )
656       return ctx
657
658    def get_concept(self, nomsd):
659       """
660           Méthode pour recuperer un concept à partir de son nom
661           dans le contexte du jdc connu avant l'exécution de la macro courante.
662       """
663       # chercher dans self.get_contexte_avant, puis si non trouve
664       # self.parent.get_concept est peut-etre plus performant
665       return self.get_contexte_courant().get(nomsd.strip(), None)
666
667    def copy(self):
668       """ Méthode qui retourne une copie de self non enregistrée auprès du JDC
669           et sans sd
670           On surcharge la methode de ETAPE pour exprimer que les concepts crees
671           par la MACRO d'origine ne sont pas crees par la copie mais eventuellement
672           seulement utilises
673       """
674       etape=N_ETAPE.ETAPE.copy(self)
675       etape.sdprods=[]
676       return etape
677
678    def copy_intern(self,etape):
679       """ Cette méthode effectue la recopie des etapes internes d'une macro
680           passée en argument (etape)
681       """
682       self.etapes=[]
683       self.index_etapes={}
684       for etp in etape.etapes:
685           new_etp=etp.copy()
686           new_etp.copy_reuse(etp)
687           new_etp.copy_sdnom(etp)
688           new_etp.reparent(self)
689           if etp.sd:
690              new_sd = etp.sd.__class__(etape=new_etp)
691              new_etp.sd = new_sd
692              if etp.reuse:
693                 new_sd.set_name(etp.sd.nom)
694              else:
695                 self.NommerSdprod(new_sd,etp.sd.nom)
696           new_etp.copy_intern(etp)
697           self.etapes.append(new_etp)
698           self.index_etapes[new_etp] = len(self.etapes) - 1
699
700
701    def reset_jdc(self,new_jdc):
702        """
703           Reinitialise l'etape avec un nouveau jdc parent new_jdc
704        """
705        if self.sd and self.reuse == None :
706            self.parent.NommerSdprod(self.sd,self.sd.nom)
707        for concept in self.sdprods:
708            self.parent.NommerSdprod(concept,concept.nom)
709
710    def reparent(self,parent):
711        """
712          Cette methode sert a reinitialiser la parente de l'objet
713        """
714        N_ETAPE.ETAPE.reparent(self,parent)
715        #on ne change pas la parenté des concepts. On s'assure uniquement que le jdc en référence est le bon
716        for concept in self.sdprods:
717            concept.jdc=self.jdc
718        for e in self.etapes:
719            e.reparent(self)
720
721    def update_const_context(self, d):
722       """
723          Met à jour le contexte des constantes pour l'évaluation de
724          formules dans la macro.
725       """
726       # Dans le jdc, const_context est mis à jour par exec_compile
727       # Dans la macro, on n'a pas le code à compiler pour récupèrer les
728       # constantes locales à la macro. On demande donc explicitement de
729       # définir les constantes "locales".
730       self.macro_const_context.update(d)
731
732    def sd_accessible(self):
733       """On peut acceder aux "valeurs" (jeveux) des ASSD dans
734       les macro-commandes qui sont localement en PAR_LOT="NON"
735       sauf pour INCLUDE.
736       """
737       if CONTEXT.debug: print ' `- MACRO sd_accessible :', self.nom
738       return self.parent.sd_accessible() or not self.is_include()
739