]> SALOME platform Git repositories - tools/eficas.git/blobdiff - Noyau/N_MACRO_ETAPE.py
Salome HOME
travail sur monPlusieurs
[tools/eficas.git] / Noyau / N_MACRO_ETAPE.py
index 64d875aaff8e9cca55e0067adbe62266646f00f9..ee2e2043506bb15af692cae42c158107608bdfa9 100644 (file)
@@ -1,24 +1,24 @@
-#@ MODIF N_MACRO_ETAPE Noyau  DATE 04/02/2004   AUTEUR CAMBIER S.CAMBIER 
-#            CONFIGURATION MANAGEMENT OF EDF VERSION
-# ======================================================================
-# COPYRIGHT (C) 1991 - 2002  EDF R&D                  WWW.CODE-ASTER.ORG
-# THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
-# IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
-# THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR   
-# (AT YOUR OPTION) ANY LATER VERSION.                                 
+# -*- coding: iso-8859-1 -*-
+# Copyright (C) 2007-2013   EDF R&D
 #
 #
-# THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT 
-# WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF          
-# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU    
-# GENERAL PUBLIC LICENSE FOR MORE DETAILS.                            
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
 #
 #
-# YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE   
-# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,       
-#    1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.      
-#                                                                       
-#                                                                       
-# ======================================================================
-""" 
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+"""
     Ce module contient la classe MACRO_ETAPE qui sert à vérifier et à exécuter
     une commande
 """
     Ce module contient la classe MACRO_ETAPE qui sert à vérifier et à exécuter
     une commande
 """
@@ -26,6 +26,7 @@
 # Modules Python
 import types,sys,string
 import traceback
 # Modules Python
 import types,sys,string
 import traceback
+from warnings import warn
 
 # Modules EFICAS
 import N_MCCOMPO
 
 # Modules EFICAS
 import N_MCCOMPO
@@ -33,102 +34,91 @@ import N_ETAPE
 from N_Exception import AsException
 import N_utils
 from N_utils import AsType
 from N_Exception import AsException
 import N_utils
 from N_utils import AsType
+from N_CO import CO
+from N_ASSD import ASSD
+from N_info import message, SUPERV
 
 class MACRO_ETAPE(N_ETAPE.ETAPE):
    """
 
    """
    nature = "COMMANDE"
 
 class MACRO_ETAPE(N_ETAPE.ETAPE):
    """
 
    """
    nature = "COMMANDE"
+   typeCO=CO
    def __init__(self,oper=None,reuse=None,args={}):
       """
    def __init__(self,oper=None,reuse=None,args={}):
       """
-         Attributs :
-
-          - definition : objet portant les attributs de définition d'une étape 
-                         de type macro-commande. Il est initialisé par 
-                          l'argument oper.
-
-          - reuse : indique le concept d'entrée réutilisé. Il se trouvera donc
-                    en sortie si les conditions d'exécution de l'opérateur 
-                    l'autorise
-
-          - valeur : arguments d'entrée de type mot-clé=valeur. Initialisé 
-                     avec l'argument args.
-
-      """
-      self.definition=oper
-      self.reuse=reuse
-      self.valeur=args
-      self.nettoiargs()
-      self.parent=CONTEXT.get_current_step()
-      self.etape = self
-      self.nom=oper.nom
-      self.idracine=oper.label
-      self.appel=N_utils.callee_where()
-      self.mc_globaux={}
-      self.g_context={}
+      Attributs :
+         - definition : objet portant les attributs de définition d'une étape
+           de type macro-commande. Il est initialisé par
+           l'argument oper.
+         - reuse : indique le concept d'entrée réutilisé. Il se trouvera donc
+           en sortie si les conditions d'exécution de l'opérateur
+           l'autorise
+         - valeur : arguments d'entrée de type mot-clé=valeur. Initialisé
+           avec l'argument args.
+      """
+      N_ETAPE.ETAPE.__init__(self, oper, reuse, args, niveau=5)
+      self.g_context = {}
       # Contexte courant
       # Contexte courant
-      self.current_context={}
-      self.index_etape_courante=0
-      self.etapes=[]
-      self.sds=[]
-      #  Dans le cas d'une macro écrite en Python, l'attribut Outputs est un 
-      #  dictionnaire qui contient les concepts produits de sortie 
+      self.current_context = {}
+      self.macro_const_context = {}
+      self.index_etape_courante = 0
+      self.etapes = []
+      self.index_etapes = {}
+      #  Dans le cas d'une macro écrite en Python, l'attribut Outputs est un
+      #  dictionnaire qui contient les concepts produits de sortie
       #  (nom : ASSD) déclarés dans la fonction sd_prod
       #  (nom : ASSD) déclarés dans la fonction sd_prod
-      self.Outputs={}
-      self.sd=None
-      self.actif=1
-      self.sdprods=[]
-      self.make_register()
+      self.Outputs = {}
+      self.sdprods = []
+      self.UserError = "UserError"
+      # permet de stocker le nom du dernier concept nommé dans la macro
+      self.last = None
 
    def make_register(self):
       """
 
    def make_register(self):
       """
-         Initialise les attributs jdc, id, niveau et réalise les enregistrements
-         nécessaires
+      Initialise les attributs jdc, id, niveau et réalise les enregistrements
+      nécessaires
       """
       """
+      N_ETAPE.ETAPE.make_register(self)
       if self.parent :
       if self.parent :
-         self.jdc = self.parent.get_jdc_root()
-         self.id=self.parent.register(self)
-         self.niveau=None
+         self.UserError=self.jdc.UserError
       else:
       else:
-         self.jdc = self.parent =None
-         self.id=None
-         self.niveau=None
+         self.UserError="UserError"
 
    def Build_sd(self,nom):
       """
 
    def Build_sd(self,nom):
       """
-         Construit le concept produit de l'opérateur. Deux cas 
+         Construit le concept produit de l'opérateur. Deux cas
          peuvent se présenter :
          peuvent se présenter :
-        
-         - le parent n'est pas défini. Dans ce cas, l'étape prend en charge 
-           la création et le nommage du concept.
 
 
-         - le parent est défini. Dans ce cas, l'étape demande au parent la 
-           création et le nommage du concept.
+           - le parent n'est pas défini. Dans ce cas, l'étape prend en charge
+             la création et le nommage du concept.
+
+           - le parent est défini. Dans ce cas, l'étape demande au parent la
+             création et le nommage du concept.
 
       """
 
       """
-      if not self.isactif():return
+      #message.debug(SUPERV, "%s", self.nom)
       self.sdnom=nom
       try:
       self.sdnom=nom
       try:
-         # On positionne la macro self en tant que current_step pour que les 
+         # On positionne la macro self en tant que current_step pour que les
          # étapes créées lors de l'appel à sd_prod et à op_init aient la macro
          # étapes créées lors de l'appel à sd_prod et à op_init aient la macro
-         #  comme parent 
+         #  comme parent
          self.set_current_step()
          if self.parent:
             sd= self.parent.create_sdprod(self,nom)
          self.set_current_step()
          if self.parent:
             sd= self.parent.create_sdprod(self,nom)
-            if type(self.definition.op_init) == types.FunctionType: 
+            if type(self.definition.op_init) == types.FunctionType:
                apply(self.definition.op_init,(self,self.parent.g_context))
          else:
             sd=self.get_sd_prod()
             if sd != None and self.reuse == None:
                apply(self.definition.op_init,(self,self.parent.g_context))
          else:
             sd=self.get_sd_prod()
             if sd != None and self.reuse == None:
-               # On ne nomme le concept que dans le cas de non reutilisation 
+               # On ne nomme le concept que dans le cas de non reutilisation
                # d un concept
                # d un concept
-               sd.nom=nom
+               sd.set_name(nom)
          self.reset_current_step()
       except AsException,e:
          self.reset_current_step()
          raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
                               'fichier : ',self.appel[1],e)
          self.reset_current_step()
       except AsException,e:
          self.reset_current_step()
          raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
                               'fichier : ',self.appel[1],e)
-      except (EOFError,self.jdc.UserError):
+      except (EOFError, self.UserError):
          # Le retablissement du step courant n'est pas strictement necessaire. On le fait pour des raisons de coherence
          self.reset_current_step()
          raise
          # Le retablissement du step courant n'est pas strictement necessaire. On le fait pour des raisons de coherence
          self.reset_current_step()
          raise
@@ -146,20 +136,23 @@ class MACRO_ETAPE(N_ETAPE.ETAPE):
       """
         Retourne le concept résultat d'une macro étape
         La difference avec une etape ou une proc-etape tient a ce que
       """
         Retourne le concept résultat d'une macro étape
         La difference avec une etape ou une proc-etape tient a ce que
-         le concept produit peut exister ou pas
+        le concept produit peut exister ou pas
+
         Si sd_prod == None le concept produit n existe pas on retourne None
         Si sd_prod == None le concept produit n existe pas on retourne None
+
         Deux cas :
         Deux cas :
-         cas 1 : sd_prod  n'est pas une fonction
+         cas 1 : sd_prod  n'est pas une fonction
                  il s'agit d'une sous classe de ASSD
                  on construit le sd à partir de cette classe
                  et on le retourne
                  il s'agit d'une sous classe de ASSD
                  on construit le sd à partir de cette classe
                  et on le retourne
-         cas 2 : sd_prod est une fonction
-                  on l'évalue avec les mots-clés de l'étape (mc_liste)
+         cas 2 : sd_prod est une fonction
+                 on l'évalue avec les mots-clés de l'étape (mc_liste)
                  on construit le sd à partir de la classe obtenue
                  et on le retourne
       """
       sd_prod=self.definition.sd_prod
       self.typret=None
                  on construit le sd à partir de la classe obtenue
                  et on le retourne
       """
       sd_prod=self.definition.sd_prod
       self.typret=None
+
       if type(self.definition.sd_prod) == types.FunctionType:
         d=self.cree_dict_valeurs(self.mc_liste)
         try:
       if type(self.definition.sd_prod) == types.FunctionType:
         d=self.cree_dict_valeurs(self.mc_liste)
         try:
@@ -168,7 +161,7 @@ class MACRO_ETAPE(N_ETAPE.ETAPE):
           # les concepts produits dans self.sdprods, il faut le mettre à zéro avant de l'appeler
           self.sdprods=[]
           sd_prod= apply(sd_prod,(self,),d)
           # les concepts produits dans self.sdprods, il faut le mettre à zéro avant de l'appeler
           self.sdprods=[]
           sd_prod= apply(sd_prod,(self,),d)
-        except (EOFError,self.jdc.UserError):
+        except (EOFError,self.UserError):
           raise
         except:
           if CONTEXT.debug: traceback.print_exc()
           raise
         except:
           if CONTEXT.debug: traceback.print_exc()
@@ -186,33 +179,47 @@ class MACRO_ETAPE(N_ETAPE.ETAPE):
         else:
           self.sd= sd_prod(etape=self)
           self.typret=sd_prod
         else:
           self.sd= sd_prod(etape=self)
           self.typret=sd_prod
-          # Si la commande est obligatoirement reentrante et reuse n'a pas ete specifie, c'est une erreur. 
-          # On ne fait rien ici. L'erreur sera traitee par la suite. 
+          # Si la commande est obligatoirement reentrante et reuse n'a pas ete specifie, c'est une erreur.
+          # On ne fait rien ici. L'erreur sera traitee par la suite.
+      # précaution
+      if self.sd is not None and not isinstance(self.sd, ASSD):
+         raise AsException("""
+Impossible de typer le résultat !
+Causes possibles :
+   Utilisateur : Soit la valeur fournie derrière "reuse" est incorrecte,
+                 soit il y a une "," à la fin d'une commande précédente.
+   Développeur : La fonction "sd_prod" retourne un type invalide.""")
       return self.sd
 
    def get_type_produit(self,force=0):
       return self.sd
 
    def get_type_produit(self,force=0):
+      try:
+          return self.get_type_produit_brut(force)
+      except:
+          #traceback.print_exc()
+          return None
+
+   def get_type_produit_brut(self,force=0):
       """
            Retourne le type du concept résultat de l'étape et eventuellement type
       """
            Retourne le type du concept résultat de l'étape et eventuellement type
-            les concepts produits "à droite" du signe égal (en entrée)
+           les concepts produits "à droite" du signe égal (en entrée)
+
            Deux cas :
            Deux cas :
-            cas 1 : sd_prod de oper n'est pas une fonction
+             - cas 1 : sd_prod de oper n'est pas une fonction
                     il s'agit d'une sous classe de ASSD
                     on retourne le nom de la classe
                     il s'agit d'une sous classe de ASSD
                     on retourne le nom de la classe
-            cas 2 : il s'agit d'une fonction
+             - cas 2 : il s'agit d'une fonction
                     on l'évalue avec les mots-clés de l'étape (mc_liste)
                     et on retourne son résultat
       """
                     on l'évalue avec les mots-clés de l'étape (mc_liste)
                     et on retourne son résultat
       """
-      if not force and hasattr(self,'typret'): return self.typret
+      if not force and hasattr(self,'typret'):
+          return self.typret
+
       if type(self.definition.sd_prod) == types.FunctionType:
         d=self.cree_dict_valeurs(self.mc_liste)
       if type(self.definition.sd_prod) == types.FunctionType:
         d=self.cree_dict_valeurs(self.mc_liste)
-        try:
-          # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
-          # les concepts produits dans self.sdprods, il faut le mettre à zéro
-          self.sdprods=[]
-          sd_prod= apply(self.definition.sd_prod,(self,),d)
-        except:
-          #traceback.print_exc()
-          return None
+        # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
+        # les concepts produits dans self.sdprods, il faut le mettre à zéro
+        self.sdprods=[]
+        sd_prod= apply(self.definition.sd_prod,(self,),d)
       else:
         sd_prod=self.definition.sd_prod
       return sd_prod
       else:
         sd_prod=self.definition.sd_prod
       return sd_prod
@@ -224,34 +231,33 @@ class MACRO_ETAPE(N_ETAPE.ETAPE):
           On tient compte des commandes qui modifient le contexte
           comme DETRUIRE ou les macros
       """
           On tient compte des commandes qui modifient le contexte
           comme DETRUIRE ou les macros
       """
-      # L'étape courante pour laquelle le contexte a été calculé est 
+      # L'étape courante pour laquelle le contexte a été calculé est
       # mémorisée dans self.index_etape_courante
       # mémorisée dans self.index_etape_courante
-      # Si on insère des commandes (par ex, dans EFICAS), il faut
-      # préalablement remettre ce pointeur à 0
-      if etape:
-         index_etape=self.etapes.index(etape)
-      else:
-         index_etape=len(self.etapes)
-
-      if index_etape >= self.index_etape_courante:
-         # On calcule le contexte en partant du contexte existant
-         d=self.current_context
-         liste_etapes=self.etapes[self.index_etape_courante:index_etape]
-      else:
-         d=self.current_context={}
-         liste_etapes=self.etapes
-
-      for e in liste_etapes:
-        if e is etape:
-           break
-        if e.isactif():
-           e.update_context(d)
-      self.index_etape_courante=index_etape
+      #message.debug(SUPERV, "g_context : %s", [k for k, v in self.g_context.items() if isinstance(v, ASSD)])
+      #message.debug(SUPERV, "current_context : %s", [k for k, v in self.current_context.items() if isinstance(v, ASSD)])
+      d = self.current_context = self.g_context.copy()
+      if etape is None:
+          return d
+      # retirer les sd produites par 'etape'
+      sd_names = [sd.nom for sd in etape.get_created_sd()]
+      #message.debug(SUPERV, "etape: %s, reuse : %s, sdprods de %s : %s",
+                    #self.nom, etape.reuse, etape.nom, sd_names)
+      for nom in sd_names:
+         try:
+             del d[nom]
+         except KeyError:
+             pass
+             # Exemple avec INCLUDE_MATERIAU appelé dans une macro.
+             # Les fonctions restent uniquement dans le contexte de INCLUDE_MATERIAU,
+             # elles ne sont donc pas dans le contexte de la macro appelante.
+             #from warnings import warn
+             #warn("concept '%s' absent du contexte de %s" % (nom, self.nom),
+                  #RuntimeWarning, stacklevel=2)
       return d
 
    def supprime(self):
       """
       return d
 
    def supprime(self):
       """
-         Méthode qui supprime toutes les références arrières afin que 
+         Méthode qui supprime toutes les références arrières afin que
          l'objet puisse etre correctement détruit par le garbage collector
       """
       N_MCCOMPO.MCCOMPO.supprime(self)
          l'objet puisse etre correctement détruit par le garbage collector
       """
       N_MCCOMPO.MCCOMPO.supprime(self)
@@ -263,48 +269,130 @@ class MACRO_ETAPE(N_ETAPE.ETAPE):
       for etape in self.etapes:
          etape.supprime()
 
       for etape in self.etapes:
          etape.supprime()
 
+   def clean(self, netapes):
+      """Nettoie les `netapes` dernières étapes de la liste des étapes."""
+      if self.jdc.hist_etape:
+          return
+      for i in xrange(netapes):
+        e=self.etapes.pop()
+        jdc=e.jdc
+        parent=e.parent
+        e.supprime()
+        e.parent=parent
+        e.jdc=jdc
+        #message.debug(SUPERV, "MACRO.clean - etape = %s - refcount(e) = %d",
+                      #e.nom, sys.getrefcount(e))
+        del self.index_etapes[e]
+
    def type_sdprod(self,co,t):
       """
            Cette methode a pour fonction de typer le concept co avec le type t
    def type_sdprod(self,co,t):
       """
            Cette methode a pour fonction de typer le concept co avec le type t
-            dans les conditions suivantes
-            1- co est un concept produit de self
-            2- co est un concept libre : on le type et on l attribue à self
+           dans les conditions suivantes :
+            1. co est un concept produit de self
+            2. co est un concept libre : on le type et on l attribue à self
+
            Elle enregistre egalement les concepts produits (on fait l hypothese
            Elle enregistre egalement les concepts produits (on fait l hypothese
-            que la liste sdprods a été correctement initialisee, vide probablement)
+           que la liste sdprods a été correctement initialisee, vide probablement)
       """
       if not hasattr(co,'etape'):
          # Le concept vaut None probablement. On ignore l'appel
          return
       """
       if not hasattr(co,'etape'):
          # Le concept vaut None probablement. On ignore l'appel
          return
-
+      #
+      # On cherche a discriminer les differents cas de typage d'un concept
+      # produit par une macro qui est specifie dans un mot cle simple.
+      # On peut passer plusieurs fois par type_sdprod ce qui explique
+      # le nombre important de cas.
+      #
+      # Cas 1 : Le concept est libre. Il vient d'etre cree par CO(nom)
+      # Cas 2 : Le concept est produit par la macro. On est deja passe par type_sdprod.
+      #         Cas semblable a Cas 1.
+      # Cas 3 : Le concept est produit par la macro englobante (parent). On transfere
+      #         la propriete du concept de la macro parent a la macro courante (self)
+      #         en verifiant que le type est valide
+      # Cas 4 : La concept est la propriete d'une etape fille. Ceci veut dire qu'on est
+      #         deja passe par type_sdprod et que la propriete a ete transfere a une
+      #         etape fille. Cas semblable a Cas 3.
+      # Cas 5 : Le concept est produit par une etape externe a la macro.
+      #
       if co.etape == None:
       if co.etape == None:
-         # le concept est libre
-         co.etape=self
-         co.__class__ = t
+         # Cas 1 : le concept est libre
+         # On l'attache a la macro et on change son type dans le type demande
+         # Recherche du mot cle simple associe au concept
+         mcs=self.get_mcs_with_co(co)
+         if len(mcs) != 1:
+            raise AsException("""Erreur interne.
+Il ne devrait y avoir qu'un seul mot cle porteur du concept CO (%s)""" % co)
+         mcs=mcs[0]
+         if not self.typeCO in mcs.definition.type:
+            raise AsException("""Erreur interne.
+Impossible de changer le type du concept (%s). Le mot cle associe ne supporte pas CO mais seulement (%s)""" %(co,mcs.definition.type))
+         co.etape = self
+         # affectation du bon type du concept
+         #message.debug(SUPERV, "MACRO.type_sdprod : changement de type de %s --> %s", co, t)
+         co.change_type(t)
          self.sdprods.append(co)
          self.sdprods.append(co)
-      elif co.etape== self:
-         # le concept est produit par self
-         co.__class__ = t
+
+      elif co.etape == self:
+         # Cas 2 : le concept est produit par la macro (self)
+         # On est deja passe par type_sdprod (Cas 1 ou 3).
+         #XXX Peut-il être créer par une autre macro ?
+         #    On vérifie juste que c'est un vrai CO non déjà typé
+         #if co.etape == co._etape: 
+         if co.is_typco() == 1:
+           #Le concept a été créé par la macro (self)
+           #On peut changer son type
+           co.change_type(t)
+         else:
+           #Le concept a été créé par une macro parente
+           # Le type du concept doit etre coherent avec le type demande (seulement derive)
+           if not isinstance(co,t):
+             raise AsException("""Erreur interne.
+Le type demande (%s) et le type du concept (%s) devraient etre derives""" %(t,co.__class__))
+
          self.sdprods.append(co)
          self.sdprods.append(co)
+
       elif co.etape== self.parent:
       elif co.etape== self.parent:
-         # le concept est produit par la macro superieure
-         # on transfere la propriete
-         # On verifie que le type du concept existant co.__class__ est un sur type de celui attendu
+         # Cas 3 : le concept est produit par la macro parente (self.parent)
+         # on transfere la propriete du concept a la macro fille
+         # et on change le type du concept comme demande
+         # Au prealable, on verifie que le concept existant (co) est une instance
+         # possible du type demande (t)
          # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
          # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
-         if not issubclass(t,co.__class__):
-            raise AsException("Le type du concept produit %s devrait etre une sur classe de %s" %(co.__class__,t))
+         if not isinstance(co,t):
+            raise AsException("""
+Impossible de changer le type du concept produit (%s) en (%s).
+Le type actuel (%s) devrait etre une classe derivee du nouveau type (%s)""" % (co,t,co.__class__,t))
+         mcs=self.get_mcs_with_co(co)
+         if len(mcs) != 1:
+            raise AsException("""Erreur interne.
+Il ne devrait y avoir qu'un seul mot cle porteur du concept CO (%s)""" % co)
+         mcs=mcs[0]
+         if not self.typeCO in mcs.definition.type:
+            raise AsException("""Erreur interne.
+Impossible de changer le type du concept (%s). Le mot cle associe ne supporte pas CO mais seulement (%s)""" %(co,mcs.definition.type))
          co.etape=self
          co.etape=self
-         co.__class__ = t
+         # On ne change pas le type car il respecte la condition isinstance(co,t)
+         #co.__class__ = t
          self.sdprods.append(co)
          self.sdprods.append(co)
+
       elif self.issubstep(co.etape):
       elif self.issubstep(co.etape):
-         # Le concept est propriété d'une sous etape de self. Il doit etre considere
-         # comme produit par la macro => ajout dans self.sdprods
+         # Cas 4 : Le concept est propriété d'une sous etape de la macro (self).
+         # On est deja passe par type_sdprod (Cas 3 ou 1).
+         # Il suffit de le mettre dans la liste des concepts produits (self.sdprods)
+         # Le type du concept et t doivent etre derives.
+         # Il n'y a aucune raison pour que la condition ne soit pas verifiee.
+         if not isinstance(co,t):
+            raise AsException("""Erreur interne.
+Le type demande (%s) et le type du concept (%s) devraient etre derives""" %(t,co.__class__))
          self.sdprods.append(co)
          self.sdprods.append(co)
+
       else:
       else:
-         # le concept est produit par une autre étape
+         # Cas 5 : le concept est produit par une autre étape
+         # On ne fait rien
          return
 
    def issubstep(self,etape):
          return
 
    def issubstep(self,etape):
-      """ 
+      """
           Cette methode retourne un entier indiquant si etape est une
           sous etape de la macro self ou non
           1 = oui
           Cette methode retourne un entier indiquant si etape est une
           sous etape de la macro self ou non
           1 = oui
@@ -316,41 +404,42 @@ class MACRO_ETAPE(N_ETAPE.ETAPE):
       return 0
 
    def register(self,etape):
       return 0
 
    def register(self,etape):
-      """ 
-          Enregistrement de etape dans le contexte de la macro : liste etapes 
+      """
+          Enregistrement de etape dans le contexte de la macro : liste etapes
           et demande d enregistrement global aupres du JDC
       """
       self.etapes.append(etape)
           et demande d enregistrement global aupres du JDC
       """
       self.etapes.append(etape)
+      self.index_etapes[etape] = len(self.etapes) - 1
       idetape=self.jdc.g_register(etape)
       return idetape
 
    def reg_sd(self,sd):
       idetape=self.jdc.g_register(etape)
       return idetape
 
    def reg_sd(self,sd):
-      """ 
+      """
            Methode appelee dans l __init__ d un ASSD a sa creation pour
            s enregistrer (reserve aux ASSD créés au sein d'une MACRO)
       """
            Methode appelee dans l __init__ d un ASSD a sa creation pour
            s enregistrer (reserve aux ASSD créés au sein d'une MACRO)
       """
-      self.sds.append(sd)
       return self.jdc.o_register(sd)
 
    def create_sdprod(self,etape,nomsd):
       return self.jdc.o_register(sd)
 
    def create_sdprod(self,etape,nomsd):
-      """ 
-          Intention : Cette methode doit fabriquer le concept produit retourne
-                  par l'etape etape et le nommer.
-                  Elle est appelée à l'initiative de l'etape
-                  pendant le processus de construction de cette etape : methode __call__
-                  de la classe CMD (OPER ou MACRO)
-                  Ce travail est réalisé par le contexte supérieur (etape.parent)
-                  car dans certains cas, le concept ne doit pas etre fabriqué mais
-                  l'etape doit simplement utiliser un concept préexistant.
-                  Cas 1 : etape.reuse != None : le concept est réutilisé
-                  Cas 2 : l'étape appartient à une macro qui a déclaré un concept
-                          de sortie qui doit etre produit par cette etape.
+      """
+          Cette methode doit fabriquer le concept produit retourne
+          par l'etape etape et le nommer.
+
+          Elle est appelée à l'initiative de l'etape
+          pendant le processus de construction de cette etape : methode __call__
+          de la classe CMD (OPER ou MACRO)
+          Ce travail est réalisé par le contexte supérieur (etape.parent)
+          car dans certains cas, le concept ne doit pas etre fabriqué mais
+          l'etape doit simplement utiliser un concept préexistant.
+                  - Cas 1 : etape.reuse != None : le concept est réutilisé
+                  - Cas 2 : l'étape appartient à une macro qui a déclaré un concept
+                    de sortie qui doit etre produit par cette etape.
       """
       if self.Outputs.has_key(nomsd):
          # Il s'agit d'un concept de sortie de la macro. Il ne faut pas le créer
          # Il faut quand meme appeler la fonction sd_prod si elle existe.
          # get_type_produit le fait et donne le type attendu par la commande pour verification ultérieure.
       """
       if self.Outputs.has_key(nomsd):
          # Il s'agit d'un concept de sortie de la macro. Il ne faut pas le créer
          # Il faut quand meme appeler la fonction sd_prod si elle existe.
          # get_type_produit le fait et donne le type attendu par la commande pour verification ultérieure.
-         sdprod=etape.get_type_produit()
+         sdprod=etape.get_type_produit_brut()
          sd=self.Outputs[nomsd]
          # On verifie que le type du concept existant sd.__class__ est un sur type de celui attendu
          # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
          sd=self.Outputs[nomsd]
          # On verifie que le type du concept existant sd.__class__ est un sur type de celui attendu
          # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
@@ -359,24 +448,31 @@ class MACRO_ETAPE(N_ETAPE.ETAPE):
          # La propriete du concept est transferee a l'etape avec le type attendu par l'étape
          etape.sd=sd
          sd.etape=etape
          # La propriete du concept est transferee a l'etape avec le type attendu par l'étape
          etape.sd=sd
          sd.etape=etape
+         if self.reuse == sd and etape.reuse != sd \
+                and getattr(sd, "executed", 0) == 1: # n'a pas été pas détruit
+            raise AsException("Le concept '%s' est réentrant dans la macro-commande %s. " \
+                              "Il devrait donc l'être dans %s (produit sous le nom '%s')." \
+                                % (sd.nom, self.nom, etape.nom, nomsd))
          # On donne au concept le type produit par la sous commande.
          # Le principe est le suivant : apres avoir verifie que le type deduit par la sous commande
          # est bien coherent avec celui initialement affecte par la macro (voir ci dessus)
          # on affecte au concept ce type car il peut etre plus precis (derive, en general)
          sd.__class__=sdprod
          # On donne au concept le type produit par la sous commande.
          # Le principe est le suivant : apres avoir verifie que le type deduit par la sous commande
          # est bien coherent avec celui initialement affecte par la macro (voir ci dessus)
          # on affecte au concept ce type car il peut etre plus precis (derive, en general)
          sd.__class__=sdprod
-         # On force également le nom stocké dans l'attribut sdnom : on lui donne le nom 
+         # On force également le nom stocké dans l'attribut sdnom : on lui donne le nom
          # du concept associé à nomsd
          etape.sdnom=sd.nom
          # du concept associé à nomsd
          etape.sdnom=sd.nom
+         # pour l'ajouter au contexte de la macro
+         self.g_context[sd.nom] = sd
       elif etape.definition.reentrant != 'n' and etape.reuse != None:
          # On est dans le cas d'une commande avec reutilisation d'un concept existant
       elif etape.definition.reentrant != 'n' and etape.reuse != None:
          # On est dans le cas d'une commande avec reutilisation d'un concept existant
-         # get_sd_prod fait le necessaire : verifications, associations, etc. mais ne cree 
+         # get_sd_prod fait le necessaire : verifications, associations, etc. mais ne cree
          # pas un nouveau concept. Il retourne le concept reutilise
          sd= etape.get_sd_prod()
          # Dans le cas d'un concept nomme automatiquement : _xxx, __xxx,
          # pas un nouveau concept. Il retourne le concept reutilise
          sd= etape.get_sd_prod()
          # Dans le cas d'un concept nomme automatiquement : _xxx, __xxx,
-         # On force le nom stocke dans l'attribut sdnom  de l'objet etape : on lui donne le nom 
+         # On force le nom stocke dans l'attribut sdnom  de l'objet etape : on lui donne le nom
          # du concept  reutilise (sd ou etape.reuse c'est pareil)
          # Ceci est indispensable pour eviter des erreurs lors des verifications des macros
          # du concept  reutilise (sd ou etape.reuse c'est pareil)
          # Ceci est indispensable pour eviter des erreurs lors des verifications des macros
-         # En effet une commande avec reutilisation d'un concept verifie que le nom de 
+         # En effet une commande avec reutilisation d'un concept verifie que le nom de
          # la variable a gauche du signe = est le meme que celui du concept reutilise.
          # Lorsqu'une telle commande apparait dans une macro, on supprime cette verification.
          if (etape.sdnom == '' or etape.sdnom[0] == '_'):
          # la variable a gauche du signe = est le meme que celui du concept reutilise.
          # Lorsqu'une telle commande apparait dans une macro, on supprime cette verification.
          if (etape.sdnom == '' or etape.sdnom[0] == '_'):
@@ -389,65 +485,60 @@ class MACRO_ETAPE(N_ETAPE.ETAPE):
       return sd
 
    def NommerSdprod(self,sd,sdnom,restrict='non'):
       return sd
 
    def NommerSdprod(self,sd,sdnom,restrict='non'):
-      """ 
-          Cette methode est appelee par les etapes internes de la macro
-          La macro appelle le JDC pour valider le nommage
-          On considere que l espace de nom est unique et géré par le JDC
-          Si le nom est deja utilise, l appel leve une exception
-          Si restrict=='non', on insere le concept dans le contexte de la macro
-          Si restrict=='oui', on n'insere pas le concept dans le contexte de la macro
-      """
-      # Normalement, lorsqu'on appelle cette methode, on ne veut nommer que des concepts nouvellement crees.
-      # Le filtrage sur les concepts a creer ou a ne pas creer est fait dans la methode
-      # create_sdprod. La seule chose a verifier apres conversion eventuelle du nom
-      # est de verifier que le nom n'est pas deja attribue. Ceci est fait en delegant
-      # au JDC par l'intermediaire du parent.
-
-      #XXX attention inconsistence : prefix et gcncon ne sont pas 
-      # définis dans le package Noyau. La methode NommerSdprod pour
-      # les macros devrait peut etre etre déplacée dans Build ???
-
-      if CONTEXT.debug : print "MACRO.NommerSdprod: ",sd,sdnom
-
-      if hasattr(self,'prefix'):
-        # Dans le cas de l'include_materiau on ajoute un prefixe au nom du concept
-        if sdnom != self.prefix:sdnom=self.prefix+sdnom
-
-      if self.Outputs.has_key(sdnom):
-        # Il s'agit d'un concept de sortie de la macro produit par une sous commande
-        sdnom=self.Outputs[sdnom].nom
-      elif sdnom != '' and sdnom[0] == '_':
-        # Si le nom du concept commence par le caractere _ on lui attribue
-        # un identificateur JEVEUX construit par gcncon et respectant
-        # la regle gcncon legerement adaptee ici
-        # nom commencant par __ : il s'agit de concepts qui seront detruits
-        # nom commencant par _ : il s'agit de concepts intermediaires qui seront gardes
-        # ATTENTION : il faut traiter différemment les concepts dont le nom
-        # commence par _ mais qui sont des concepts nommés automatiquement par
-        # une éventuelle sous macro.
-        # Le test suivant n'est pas tres rigoureux mais permet de fonctionner pour le moment (a améliorer)
-        if sdnom[1] in string.digits:
-          # Ce concept provient probablement d'une macro appelee par self
-          pass
-        elif sdnom[1] == '_':
-          sdnom=self.gcncon('.')
+        """
+          Cette méthode est appelée par les etapes internes de la macro.
+          La macro appelle le JDC pour valider le nommage.
+          On considère que l'espace de nom est unique et géré par le JDC.
+          Si le nom est déjà utilisé, l'appel lève une exception.
+          Si restrict=='non', on insère le concept dans le contexte du parent de la macro.
+          Si restrict=='oui', on insère le concept uniquement dans le contexte de la macro.
+        """
+        # Normalement, lorsqu'on appelle cette methode, on ne veut nommer que des concepts nouvellement crees.
+        # Le filtrage sur les concepts a creer ou a ne pas creer est fait dans la methode
+        # create_sdprod. La seule chose a verifier apres conversion eventuelle du nom
+        # est de verifier que le nom n'est pas deja attribue. Ceci est fait en delegant
+        # au JDC par l'intermediaire du parent.
+        #message.debug(SUPERV, "macro results = %s, (sdnom: %r, restrict: %r)",
+                      #self.Outputs.keys(), sdnom, restrict)
+        if self.Outputs.has_key(sdnom):
+            # Il s'agit d'un concept de sortie de la macro produit par une sous commande
+            sdnom = self.Outputs[sdnom].nom
+        elif len(sdnom) > 0:
+            if sdnom[0] in ('_', '.') and sdnom[1:].isdigit():
+                # il est déjà de la forme _9000012 ou .9000017
+                pass
+            elif sdnom[0] == '_':
+                # Si le nom du concept commence par le caractère '_', on lui attribue
+                # un identificateur JEVEUX construit par gcncon.
+                # nom commençant par __ : il s'agit de concepts qui seront détruits
+                # nom commençant par _ : il s'agit de concepts intermediaires qui seront gardés
+                if len(sdnom) > 1 and sdnom[1] == '_':
+                    sdnom = self.gcncon('.')
+                else:
+                    sdnom = self.gcncon('_')
+            elif self.nom in ('INCLUDE', 'MACR_RECAL'):
+                # dans le cas d'INCLUDE, on passe
+                # MACR_RECAL fonctionne comme INCLUDE
+                pass
+            else:
+                # On est dans le cas d'un nom de concept global
+                #XXX à voir, création de CO() dans CALC_ESSAI (sdls139a)
+                if not sd.is_typco():
+                    raise AsException("Résultat non déclaré par la macro %s : %s" % (self.nom, sdnom))
+        self.last = sdnom
+        if restrict == 'non':
+            # On demande le nommage au parent mais sans ajout du concept dans le contexte du parent
+            # car on va l'ajouter dans le contexte de la macro
+            self.parent.NommerSdprod(sd,sdnom,restrict='oui')
+            # On ajoute dans le contexte de la macro les concepts nommes
+            # Ceci est indispensable pour les CO (macro) dans un INCLUDE
+            self.g_context[sdnom]=sd
+            #message.debug(SUPERV, "g_context[%s] = %s", sdnom, sd)
         else:
         else:
-          sdnom=self.gcncon('_')
-      else:
-        # On est dans le cas d'un nom de concept global. 
-        pass
-
-      if restrict == 'non':
-         # On demande le nommage au parent mais sans ajout du concept dans le contexte du parent
-         # car on va l'ajouter dans le contexte de la macro
-         self.parent.NommerSdprod(sd,sdnom,restrict='oui')
-         # On ajoute dans le contexte de la macro les concepts nommes
-         # Ceci est indispensable pour les CO (macro) dans un INCLUDE
-         self.g_context[sdnom]=sd
-      else:
-         # La demande de nommage vient probablement d'une macro qui a mis
-         # le concept dans son contexte. On ne traite plus que le nommage (restrict="oui")
-         self.parent.NommerSdprod(sd,sdnom,restrict='oui')
+            # La demande de nommage vient probablement d'une macro qui a mis
+            # le concept dans son contexte. On ne traite plus que le nommage (restrict="oui")
+            #message.debug(SUPERV, "restrict=oui  co[%s] = %s", sdnom, sd)
+            self.parent.NommerSdprod(sd,sdnom,restrict='oui')
 
    def delete_concept_after_etape(self,etape,sd):
       """
 
    def delete_concept_after_etape(self,etape,sd):
       """
@@ -459,6 +550,21 @@ class MACRO_ETAPE(N_ETAPE.ETAPE):
       # Dans d'autres conditions, il faudrait surcharger cette méthode.
       return
 
       # Dans d'autres conditions, il faudrait surcharger cette méthode.
       return
 
+   def get_created_sd(self):
+      """Retourne la liste des sd réellement produites par l'étape.
+      Si reuse est présent, `self.sd` a été créée avant, donc n'est pas dans
+      cette liste."""
+      sdprods = self.sdprods[:]
+      if not self.reuse and self.sd:
+          sdprods.append(self.sd)
+      return sdprods
+
+   def get_last_concept(self):
+       """Retourne le dernier concept produit dans la macro.
+       Peut-être utile pour accéder au contenu 'fortran' dans une
+       clause 'except'."""
+       return self.g_context.get(self.last, None)
+
    def accept(self,visitor):
       """
          Cette methode permet de parcourir l'arborescence des objets
    def accept(self,visitor):
       """
          Cette methode permet de parcourir l'arborescence des objets
@@ -478,27 +584,23 @@ class MACRO_ETAPE(N_ETAPE.ETAPE):
       for co in self.sdprods:
         d[co.nom]=co
 
       for co in self.sdprods:
         d[co.nom]=co
 
-   def make_include(self,unite=None):
-      """
-          Inclut un fichier dont l'unite logique est unite
-      """
-      if not unite : return
-      f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
+   def make_include(self, unite=None, fname=None):
+      """Inclut un fichier dont l'unite logique est `unite` ou de nom `fname`"""
+      if unite is not None:
+         warn("'unite' is deprecated, please use 'fname' instead",
+              DeprecationWarning, stacklevel=2)
+         fname = 'fort.%s' % unite
+      if not fname:
+         return
+      f, text = self.get_file(fic_origine=self.parent.nom, fname=fname)
       self.fichier_init = f
       self.fichier_init = f
-      if f == None:return
-      self.make_contexte(f,text)
+      if f == None:
+         return
+      self.make_contexte(f, text)
 
    def make_poursuite(self):
 
    def make_poursuite(self):
-      """
-          Inclut un fichier poursuite
-      """
-      try:
-         f,text=self.get_file(fic_origine=self.parent.nom)
-      except:
-         raise AsException("Impossible d'ouvrir la base pour une poursuite")
-      self.fichier_init=f
-      if f == None:return
-      self.make_contexte(f,text)
+      """Inclut un fichier poursuite"""
+      raise NotImplementedError('this method must be derivated (in Eficas)')
 
    def make_contexte(self,f,text):
       """
 
    def make_contexte(self,f,text):
       """
@@ -510,12 +612,13 @@ class MACRO_ETAPE(N_ETAPE.ETAPE):
       # on execute le texte fourni dans le contexte forme par
       # le contexte de l etape pere (global au sens Python)
       # et le contexte de l etape (local au sens Python)
       # on execute le texte fourni dans le contexte forme par
       # le contexte de l etape pere (global au sens Python)
       # et le contexte de l etape (local au sens Python)
-      code=compile(text,f,'exec')
-      d={}
-      self.g_context = d
-      self.contexte_fichier_init = d
-      globs=self.parent.get_global_contexte()
-      exec code in globs,d
+      code = compile(text,f,'exec')
+      d = self.g_context = self.macro_const_context
+      globs = self.get_global_contexte()
+      d.update(globs)
+      exec code in globs, d
+      # pour ne pas conserver des références sur tout
+      self.macro_const_context = {}
 
    def get_global_contexte(self):
       """
 
    def get_global_contexte(self):
       """
@@ -525,10 +628,47 @@ class MACRO_ETAPE(N_ETAPE.ETAPE):
       """
       # Le contexte global est forme par concatenation du contexte
       # du parent de self et de celui de l'etape elle meme (self)
       """
       # Le contexte global est forme par concatenation du contexte
       # du parent de self et de celui de l'etape elle meme (self)
-      d=self.parent.get_global_contexte()
-      d.update(self.g_context)
+      # Pour les concepts, cela ne doit rien changer. Mais pour les constantes,
+      # les valeurs de get_contexte_avant sont moins récentes que dans
+      # get_global_contexte. On prend donc la précaution de ne pas écraser
+      # ce qui y est déjà.
+      d = self.parent.get_global_contexte()
+      d.update( self.g_context )
+      d.update( [(k, v) for k, v in self.parent.get_contexte_avant(self).items()
+                        if d.get(k) is None] )
       return d
 
       return d
 
+   def get_contexte_courant(self, etape_fille_du_jdc=None):
+      """
+         Retourne le contexte tel qu'il est au moment de l'exécution de
+         l'étape courante.
+      """
+      ctx = {}
+      # update car par ricochet on modifierait jdc.current_context
+      ctx.update( self.parent.get_contexte_courant(self) )
+      # on peut mettre None car toujours en PAR_LOT='NON', donc la dernière
+      ctx.update( self.get_contexte_avant(None) )
+      return ctx
+
+   def get_concept(self, nomsd):
+      """
+          Méthode pour recuperer un concept à partir de son nom
+          dans le contexte du jdc connu avant l'exécution de la macro courante.
+      """
+      # chercher dans self.get_contexte_avant, puis si non trouve
+      # self.parent.get_concept est peut-etre plus performant
+      co = self.get_contexte_courant().get(nomsd.strip(), None)
+      if not isinstance(co, ASSD):
+          co = None
+      return co
+
+   def get_concept_by_type(self, nomsd, typesd, etape=None):
+      """
+          Méthode pour récuperer un concept à partir de son nom et de son type.
+          Il aura comme père 'etape' (ou la macro courante si etape est absente).
+      """
+      return self.parent.get_concept_by_type(nomsd, typesd, etape=etape or self)
+      
    def copy(self):
       """ Méthode qui retourne une copie de self non enregistrée auprès du JDC
           et sans sd
    def copy(self):
       """ Méthode qui retourne une copie de self non enregistrée auprès du JDC
           et sans sd
@@ -540,9 +680,65 @@ class MACRO_ETAPE(N_ETAPE.ETAPE):
       etape.sdprods=[]
       return etape
 
       etape.sdprods=[]
       return etape
 
-
-
-
-
-
+   def copy_intern(self,etape):
+      """ Cette méthode effectue la recopie des etapes internes d'une macro
+          passée en argument (etape)
+      """
+      self.etapes=[]
+      self.index_etapes={}
+      for etp in etape.etapes:
+          new_etp=etp.copy()
+          new_etp.copy_reuse(etp)
+          new_etp.copy_sdnom(etp)
+          new_etp.reparent(self)
+          if etp.sd:
+             new_sd = etp.sd.__class__(etape=new_etp)
+             new_etp.sd = new_sd
+             if etp.reuse:
+                new_sd.set_name(etp.sd.nom)
+             else:
+                self.NommerSdprod(new_sd,etp.sd.nom)
+          new_etp.copy_intern(etp)
+          self.etapes.append(new_etp)
+          self.index_etapes[new_etp] = len(self.etapes) - 1
+
+
+   def reset_jdc(self,new_jdc):
+       """
+          Reinitialise l'etape avec un nouveau jdc parent new_jdc
+       """
+       if self.sd and self.reuse == None :
+           self.parent.NommerSdprod(self.sd,self.sd.nom)
+       for concept in self.sdprods:
+           self.parent.NommerSdprod(concept,concept.nom)
+
+   def reparent(self,parent):
+       """
+         Cette methode sert a reinitialiser la parente de l'objet
+       """
+       N_ETAPE.ETAPE.reparent(self,parent)
+       #on ne change pas la parenté des concepts. On s'assure uniquement que le jdc en référence est le bon
+       for concept in self.sdprods:
+           concept.jdc=self.jdc
+       for e in self.etapes:
+           e.reparent(self)
+
+   def update_const_context(self, d):
+      """
+         Met à jour le contexte des constantes pour l'évaluation de
+         formules dans la macro.
+      """
+      # Dans le jdc, const_context est mis à jour par exec_compile
+      # Dans la macro, on n'a pas le code à compiler pour récupèrer les
+      # constantes locales à la macro. On demande donc explicitement de
+      # définir les constantes "locales".
+      self.macro_const_context.update(d)
+
+   def sd_accessible(self):
+      """On peut acceder aux "valeurs" (jeveux) des ASSD dans
+      les macro-commandes qui sont localement en PAR_LOT="NON"
+      sauf pour INCLUDE.
+      """
+      if CONTEXT.debug: print ' `- MACRO sd_accessible :', self.nom
+      return self.parent.sd_accessible() or not self.is_include()