Salome HOME
modif pour MT
[tools/eficas.git] / Noyau / N_MACRO_ETAPE.py
1 # coding=utf-8
2 # Copyright (C) 2007-2017   EDF R&D
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19
20
21 """
22     Ce module contient la classe MACRO_ETAPE qui sert a verifier et a executer
23     une commande
24 """
25
26 # Modules Python
27 from __future__ import absolute_import
28 from __future__ import print_function
29 try :
30    from builtins import str
31    from builtins import range
32 except : pass
33 import types
34 import sys
35 import traceback
36 from warnings import warn
37
38 # Modules EFICAS
39 from . import N_MCCOMPO
40 from . import N_ETAPE
41 from .N_Exception import AsException
42 from . import N_utils
43 from .N_utils import AsType
44 from .N_CO import CO
45 from .N_ASSD import ASSD
46 from six.moves import range
47
48
49 class MACRO_ETAPE(N_ETAPE.ETAPE):
50
51     """
52
53     """
54     nature = "COMMANDE"
55     typeCO = CO
56
57     def __init__(self, oper=None, reuse=None, args={}):
58         """
59         Attributs :
60            - definition : objet portant les attributs de definition d'une etape
61              de type macro-commande. Il est initialise par
62              l'argument oper.
63            - reuse : indique le concept d'entree reutilise. Il se trouvera donc
64              en sortie si les conditions d'execution de l'operateur
65              l'autorise
66            - valeur : arguments d'entree de type mot-cle=valeur. Initialise
67              avec l'argument args.
68         """
69         N_ETAPE.ETAPE.__init__(self, oper, reuse, args, niveau=5)
70         self.g_context = {}
71         # Contexte courant
72         self.current_context = {}
73         self.macro_const_context = {}
74         self.index_etape_courante = 0
75         self.etapes = []
76         self.index_etapes = {}
77         #  Dans le cas d'une macro ecrite en Python, l'attribut Outputs est un
78         #  dictionnaire qui contient les concepts produits de sortie
79         #  (nom : ASSD) declares dans la fonction sd_prod
80         self.Outputs = {}
81         self.sdprods = []
82         self.UserError = "UserError"
83         # permet de stocker le nom du dernier concept nomme dans la macro
84         self.last = None
85
86     def makeRegister(self):
87         """
88         Initialise les attributs jdc, id, niveau et realise les enregistrements
89         necessaires
90         """
91         N_ETAPE.ETAPE.makeRegister(self)
92         if self.parent:
93             self.UserError = self.jdc.UserError
94         else:
95             self.UserError = "UserError"
96
97     def buildSd(self, nom):
98         """
99            Construit le concept produit de l'operateur. Deux cas
100            peuvent se presenter :
101
102              - le parent n'est pas defini. Dans ce cas, l'etape prend en charge
103                la creation et le nommage du concept.
104
105              - le parent est defini. Dans ce cas, l'etape demande au parent la
106                creation et le nommage du concept.
107
108         """
109         self.sdnom = nom
110         try:
111             # On positionne la macro self en tant que current_step pour que les
112             # etapes creees lors de l'appel a sd_prod et a op_init aient la macro
113             #  comme parent
114             self.setCurrentStep()
115             if self.parent:
116                 sd = self.parent.createSdprod(self, nom)
117                 if type(self.definition.op_init) == types.FunctionType:
118                     self.definition.op_init(*(
119                         self, self.parent.g_context))
120             else:
121                 sd = self.getSdProd()
122                 if sd != None and self.reuse == None:
123                     # On ne nomme le concept que dans le cas de non reutilisation
124                     # d un concept
125                     sd.setName(nom)
126             self.resetCurrentStep()
127         except AsException as e:
128             self.resetCurrentStep()
129             raise AsException("Etape ", self.nom, 'ligne : ', self.appel[0],
130                               'fichier : ', self.appel[1], e)
131         #except (EOFError, self.UserError):
132         except (EOFError):
133             # Le retablissement du step courant n'est pas strictement
134             # necessaire. On le fait pour des raisons de coherence
135             self.resetCurrentStep()
136             raise
137         except:
138             self.resetCurrentStep()
139             l = traceback.format_exception(
140                 sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])
141             raise AsException("Etape ", self.nom, 'ligne : ', self.appel[0],
142                               'fichier : ', self.appel[1] + '\n',
143                               ''.join(l))
144
145         self.Execute()
146         return sd
147
148     def getSdProd(self):
149         """
150           Retourne le concept resultat d'une macro etape
151           La difference avec une etape ou une proc-etape tient a ce que
152           le concept produit peut exister ou pas
153
154           Si sd_prod == None le concept produit n existe pas on retourne None
155
156           Deux cas :
157            - cas 1 : sd_prod  n'est pas une fonction
158                    il s'agit d'une sous classe de ASSD
159                    on construit le sd a partir de cette classe
160                    et on le retourne
161            - cas 2 : sd_prod est une fonction
162                    on l'evalue avec les mots-cles de l'etape (mcListe)
163                    on construit le sd a partir de la classe obtenue
164                    et on le retourne
165         """
166         sd_prod = self.definition.sd_prod
167         self.typret = None
168
169         if type(self.definition.sd_prod) == types.FunctionType:
170             d = self.creeDictValeurs(self.mcListe)
171             try:
172                 # la sd_prod d'une macro a l'objet macro_etape lui meme en premier argument
173                 # Comme sd_prod peut invoquer la methode typeSDProd qui ajoute
174                 # les concepts produits dans self.sdprods, il faut le mettre a
175                 # zero avant de l'appeler
176                 self.sdprods = []
177                 sd_prod = sd_prod(*(self,), **d)
178             #except (EOFError, self.UserError):
179             except (EOFError):
180                 raise
181             except Exception as exc:
182                 if CONTEXT.debug:
183                     traceback.print_exc()
184                 raise AsException("impossible d affecter un type au resultat:",
185                                   str(exc))
186
187         # on teste maintenant si la SD est reutilisee ou s'il faut la creer
188         if self.definition.reentrant != 'n' and self.reuse:
189             # Le concept produit est specifie reutilise (reuse=xxx). C'est une erreur mais non fatale.
190             # Elle sera traitee ulterieurement.
191             self.sd = self.reuse
192         else:
193             if sd_prod == None:
194                 self.sd = None
195             else:
196                 self.sd = sd_prod(etape=self)
197                 self.typret = sd_prod
198                 # Si la commande est obligatoirement reentrante et reuse n'a pas ete specifie, c'est une erreur.
199                 # On ne fait rien ici. L'erreur sera traitee par la suite.
200         # precaution
201         if self.sd is not None and not isinstance(self.sd, ASSD):
202             raise AsException("""
203 Impossible de typer le resultat !
204 Causes possibles :
205    Utilisateur : Soit la valeur fournie derrière "reuse" est incorrecte,
206                  soit il y a une "," a la fin d'une commande precedente.
207    Developpeur : La fonction "sd_prod" retourne un type invalide.""")
208         return self.sd
209
210     def getType_produit(self, force=0):
211         try:
212             return self.getType_produit_brut(force)
213         except:
214             # traceback.print_exc()
215             return None
216
217     def getType_produit_brut(self, force=0):
218         """
219              Retourne le type du concept resultat de l'etape et eventuellement type
220              les concepts produits "a droite" du signe egal (en entree)
221
222              Deux cas :
223                - cas 1 : sd_prod de oper n'est pas une fonction
224                       il s'agit d'une sous classe de ASSD
225                       on retourne le nom de la classe
226                - cas 2 : il s'agit d'une fonction
227                       on l'evalue avec les mots-cles de l'etape (mcListe)
228                       et on retourne son resultat
229         """
230         if not force and hasattr(self, 'typret'):
231             return self.typret
232
233         if type(self.definition.sd_prod) == types.FunctionType:
234             d = self.creeDictValeurs(self.mcListe)
235             # Comme sd_prod peut invoquer la methode typeSDProd qui ajoute
236             # les concepts produits dans self.sdprods, il faut le mettre a zero
237             self.sdprods = []
238             sd_prod = self.definition.sd_prod(*(self,), **d)
239         else:
240             sd_prod = self.definition.sd_prod
241         return sd_prod
242
243     def getContexteAvant(self, etape):
244         """
245             Retourne le dictionnaire des concepts connus avant etape
246             pour les commandes internes a la macro
247             On tient compte des commandes qui modifient le contexte
248             comme DETRUIRE ou les macros
249         """
250         # L'etape courante pour laquelle le contexte a ete calcule est
251         # memorisee dans self.index_etape_courante
252         # self.current_context.items() if isinstance(v, ASSD)])
253         d = self.current_context = self.g_context.copy()
254         if etape is None:
255             return d
256         # retirer les sd produites par 'etape'
257         sd_names = [sd.nom for sd in etape.getCreated_sd()]
258         for nom in sd_names:
259             try:
260                 del d[nom]
261             except KeyError:
262                 pass
263                 # Exemple avec INCLUDE_MATERIAU appele dans une macro.
264                 # Les fonctions restent uniquement dans le contexte de INCLUDE_MATERIAU,
265                 # elles ne sont donc pas dans le contexte de la macro appelante.
266                 # from warnings import warn
267                 # warn("concept '%s' absent du contexte de %s" % (nom, self.nom),
268                      # RuntimeWarning, stacklevel=2)
269         return d
270
271     def supprime(self):
272         """
273            Methode qui supprime toutes les references arrières afin que
274            l'objet puisse etre correctement detruit par le garbage collector
275         """
276         N_MCCOMPO.MCCOMPO.supprime(self)
277         self.jdc = None
278         self.appel = None
279         if self.sd:
280             self.sd.supprime()
281         for concept in self.sdprods:
282             concept.supprime()
283         for etape in self.etapes:
284             etape.supprime()
285
286     def clean(self, netapes):
287         """Nettoie les `netapes` dernières etapes de la liste des etapes."""
288         if self.jdc.hist_etape:
289             return
290         for i in range(netapes):
291             e = self.etapes.pop()
292             jdc = e.jdc
293             parent = e.parent
294             e.supprime()
295             e.parent = parent
296             e.jdc = jdc
297             del self.index_etapes[e]
298
299     def typeSDProd(self, co, t):
300         """
301              Cette methode a pour fonction de typer le concept co avec le type t
302              dans les conditions suivantes :
303               1. co est un concept produit de self
304               2. co est un concept libre : on le type et on l attribue a self
305
306              Elle enregistre egalement les concepts produits (on fait l hypothese
307              que la liste sdprods a ete correctement initialisee, vide probablement)
308         """
309         if not hasattr(co, 'etape'):
310             # Le concept vaut None probablement. On ignore l'appel
311             return
312         #
313         # On cherche a discriminer les differents cas de typage d'un concept
314         # produit par une macro qui est specifie dans un mot cle simple.
315         # On peut passer plusieurs fois par typeSDProd ce qui explique
316         # le nombre important de cas.
317         #
318         # Cas 1 : Le concept est libre. Il vient d'etre cree par CO(nom)
319         # Cas 2 : Le concept est produit par la macro. On est deja passe par typeSDProd.
320         #         Cas semblable a Cas 1.
321         # Cas 3 : Le concept est produit par la macro englobante (parent). On transfere
322         #         la propriete du concept de la macro parent a la macro courante (self)
323         #         en verifiant que le type est valide
324         # Cas 4 : La concept est la propriete d'une etape fille. Ceci veut dire qu'on est
325         #         deja passe par typeSDProd et que la propriete a ete transfere a une
326         #         etape fille. Cas semblable a Cas 3.
327         # Cas 5 : Le concept est produit par une etape externe a la macro.
328         #
329         if co.etape == None:
330             # Cas 1 : le concept est libre
331             # On l'attache a la macro et on change son type dans le type demande
332             # Recherche du mot cle simple associe au concept
333             mcs = self.getMcsWithCo(co)
334             if len(mcs) != 1:
335                 raise AsException("""Erreur interne.
336 Il ne devrait y avoir qu'un seul mot cle porteur du concept CO (%s)""" % co)
337             mcs = mcs[0]
338             if not self.typeCO in mcs.definition.type:
339                 raise AsException("""Erreur interne.
340 Impossible de changer le type du concept (%s). Le mot cle associe ne supporte pas CO mais seulement (%s)""" % (co, mcs.definition.type))
341             co.etape = self
342             # affectation du bon type du concept
343             co.changeType(t)
344             self.sdprods.append(co)
345
346         elif co.etape == self:
347             # Cas 2 : le concept est produit par la macro (self)
348             # On est deja passe par typeSDProd (Cas 1 ou 3).
349             # XXX Peut-il etre creer par une autre macro ?
350             #    On verifie juste que c'est un vrai CO non deja type
351             # if co.etape == co._etape:
352             if co.isTypCO() == 1:
353                 # Le concept a ete cree par la macro (self)
354                 # On peut changer son type
355                 co.changeType(t)
356             else:
357                 # Le concept a ete cree par une macro parente
358                 # Le type du concept doit etre coherent avec le type demande
359                 # (seulement derive)
360                 if not isinstance(co, t):
361                     raise AsException("""Erreur interne.
362 Le type demande (%s) et le type du concept (%s) devraient etre derives""" % (t, co.__class__))
363
364             self.sdprods.append(co)
365
366         elif co.etape == self.parent:
367             # Cas 3 : le concept est produit par la macro parente (self.parent)
368             # on transfere la propriete du concept a la macro fille
369             # et on change le type du concept comme demande
370             # Au prealable, on verifie que le concept existant (co) est une instance
371             # possible du type demande (t)
372             # Cette règle est normalement coherente avec les règles de
373             # verification des mots-cles
374             if not isinstance(co, t):
375                 raise AsException("""
376 Impossible de changer le type du concept produit (%s) en (%s).
377 Le type actuel (%s) devrait etre une classe derivee du nouveau type (%s)""" % (co, t, co.__class__, t))
378             mcs = self.getMcsWithCo(co)
379             if len(mcs) != 1:
380                 raise AsException("""Erreur interne.
381 Il ne devrait y avoir qu'un seul mot cle porteur du concept CO (%s)""" % co)
382             mcs = mcs[0]
383             if not self.typeCO in mcs.definition.type:
384                 raise AsException("""Erreur interne.
385 Impossible de changer le type du concept (%s). Le mot cle associe ne supporte pas CO mais seulement (%s)""" % (co, mcs.definition.type))
386             co.etape = self
387             # On ne change pas le type car il respecte la condition isinstance(co,t)
388             # co.__class__ = t
389             self.sdprods.append(co)
390
391         elif self.issubstep(co.etape):
392             # Cas 4 : Le concept est propriete d'une sous etape de la macro (self).
393             # On est deja passe par typeSDProd (Cas 3 ou 1).
394             # Il suffit de le mettre dans la liste des concepts produits (self.sdprods)
395             # Le type du concept et t doivent etre derives.
396             # Il n'y a aucune raison pour que la condition ne soit pas
397             # verifiee.
398             if not isinstance(co, t):
399                 raise AsException("""Erreur interne.
400 Le type demande (%s) et le type du concept (%s) devraient etre derives""" % (t, co.__class__))
401             self.sdprods.append(co)
402
403         else:
404             # Cas 5 : le concept est produit par une autre etape
405             # On ne fait rien
406             return
407
408     def issubstep(self, etape):
409         """
410             Cette methode retourne un entier indiquant si etape est une
411             sous etape de la macro self ou non
412             1 = oui
413             0 = non
414         """
415         if etape in self.etapes:
416             return 1
417         for etap in self.etapes:
418             if etap.issubstep(etape):
419                 return 1
420         return 0
421
422     def register(self, etape):
423         """
424             Enregistrement de etape dans le contexte de la macro : liste etapes
425             et demande d enregistrement global aupres du JDC
426         """
427         self.etapes.append(etape)
428         self.index_etapes[etape] = len(self.etapes) - 1
429         idetape = self.jdc.gRegister(etape)
430         return idetape
431
432     def regSD(self, sd):
433         """
434              Methode appelee dans l __init__ d un ASSD a sa creation pour
435              s enregistrer (reserve aux ASSD crees au sein d'une MACRO)
436         """
437         return self.jdc.o_register(sd)
438
439     def createSdprod(self, etape, nomsd):
440         """
441             Cette methode doit fabriquer le concept produit retourne
442             par l'etape etape et le nommer.
443
444             Elle est appelee a l'initiative de l'etape
445             pendant le processus de construction de cette etape : methode __call__
446             de la classe CMD (OPER ou MACRO)
447             Ce travail est realise par le contexte superieur (etape.parent)
448             car dans certains cas, le concept ne doit pas etre fabrique mais
449             l'etape doit simplement utiliser un concept preexistant.
450                     - Cas 1 : etape.reuse != None : le concept est reutilise
451                     - Cas 2 : l'etape appartient a une macro qui a declare un concept
452                       de sortie qui doit etre produit par cette etape.
453         """
454         if nomsd in self.Outputs:
455             # Il s'agit d'un concept de sortie de la macro. Il ne faut pas le creer
456             # Il faut quand meme appeler la fonction sd_prod si elle existe.
457             # getType_produit le fait et donne le type attendu par la commande
458             # pour verification ulterieure.
459             sdprod = etape.getType_produit_brut()
460             sd = self.Outputs[nomsd]
461             # On verifie que le type du concept existant sd.__class__ est un sur type de celui attendu
462             # Cette règle est normalement coherente avec les règles de
463             # verification des mots-cles
464             if not issubclass(sdprod, sd.__class__):
465                 raise AsException(
466                     "Le type du concept produit %s devrait etre une sur classe de %s" % (sd.__class__, sdprod))
467             # La propriete du concept est transferee a l'etape avec le type
468             # attendu par l'etape
469             etape.sd = sd
470             sd.etape = etape
471             if self.reuse == sd and etape.reuse != sd \
472                     and getattr(sd, "executed", 0) == 1:  # n'a pas ete pas detruit
473                 raise AsException("Le concept '%s' est reentrant dans la macro-commande %s. "
474                                   "Il devrait donc l'etre dans %s (produit sous le nom '%s')."
475                                   % (sd.nom, self.nom, etape.nom, nomsd))
476             # On donne au concept le type produit par la sous commande.
477             # Le principe est le suivant : apres avoir verifie que le type deduit par la sous commande
478             # est bien coherent avec celui initialement affecte par la macro (voir ci dessus)
479             # on affecte au concept ce type car il peut etre plus precis
480             # (derive, en general)
481             sd.__class__ = sdprod
482             # On force egalement le nom stocke dans l'attribut sdnom : on lui donne le nom
483             # du concept associe a nomsd
484             etape.sdnom = sd.nom
485             # pour l'ajouter au contexte de la macro
486             self.g_context[sd.nom] = sd
487         elif etape.definition.reentrant != 'n' and etape.reuse != None:
488             # On est dans le cas d'une commande avec reutilisation d'un concept existant
489             # getSdProd fait le necessaire : verifications, associations, etc. mais ne cree
490             # pas un nouveau concept. Il retourne le concept reutilise
491             sd = etape.getSdProd()
492             # Dans le cas d'un concept nomme automatiquement : _xxx, __xxx,
493             # On force le nom stocke dans l'attribut sdnom  de l'objet etape : on lui donne le nom
494             # du concept  reutilise (sd ou etape.reuse c'est pareil)
495             # Ceci est indispensable pour eviter des erreurs lors des verifications des macros
496             # En effet une commande avec reutilisation d'un concept verifie que le nom de
497             # la variable a gauche du signe = est le meme que celui du concept reutilise.
498             # Lorsqu'une telle commande apparait dans une macro, on supprime
499             # cette verification.
500             if (etape.sdnom == '' or etape.sdnom[0] == '_'):
501                 etape.sdnom = sd.nom
502         else:
503             # On est dans le cas de la creation d'un nouveau concept
504             sd = etape.getSdProd()
505             if sd != None:
506                 self.NommerSdprod(sd, nomsd)
507         return sd
508
509     def NommerSdprod(self, sd, sdnom, restrict='non'):
510         """
511           Cette methode est appelee par les etapes internes de la macro.
512           La macro appelle le JDC pour valider le nommage.
513           On considère que l'espace de nom est unique et gere par le JDC.
514           Si le nom est deja utilise, l'appel lève une exception.
515           Si restrict=='non', on insère le concept dans le contexte du parent de la macro.
516           Si restrict=='oui', on insère le concept uniquement dans le contexte de la macro.
517         """
518         # Normalement, lorsqu'on appelle cette methode, on ne veut nommer que des concepts nouvellement crees.
519         # Le filtrage sur les concepts a creer ou a ne pas creer est fait dans la methode
520         # createSdprod. La seule chose a verifier apres conversion eventuelle du nom
521         # est de verifier que le nom n'est pas deja attribue. Ceci est fait en delegant
522         # au JDC par l'intermediaire du parent.
523         if sdnom in self.Outputs :
524                 # Il s'agit d'un concept de sortie de la macro produit par une
525                 # sous commande
526             sdnom = self.Outputs[sdnom].nom
527         elif len(sdnom) > 0:
528             if sdnom[0] in ('_', '.') and sdnom[1:].isdigit():
529                 # il est deja de la forme _9000012 ou .9000017
530                 pass
531             elif sdnom[0] == '_':
532                 # Si le nom du concept commence par le caractère '_', on lui attribue
533                 # un identificateur JEVEUX construit par gcncon.
534                 # nom commençant par __ : il s'agit de concepts qui seront detruits
535                 # nom commençant par _ : il s'agit de concepts intermediaires
536                 # qui seront gardes
537                 if len(sdnom) > 1 and sdnom[1] == '_':
538                     sdnom = self.gcncon('.')
539                 else:
540                     sdnom = self.gcncon('_')
541             elif self.nom in ('INCLUDE', 'MACR_RECAL'):
542                 # dans le cas d'INCLUDE, on passe
543                 # MACR_RECAL fonctionne comme INCLUDE
544                 pass
545             else:
546                 # On est dans le cas d'un nom de concept global
547                 # XXX a voir, creation de CO() dans CALC_ESSAI (sdls139a)
548                 if not sd.isTypCO():
549                     raise AsException(
550                         "Resultat non declare par la macro %s : %s" % (self.nom, sdnom))
551         self.last = sdnom
552         if restrict == 'non':
553             # On demande le nommage au parent mais sans ajout du concept dans le contexte du parent
554             # car on va l'ajouter dans le contexte de la macro
555             self.parent.NommerSdprod(sd, sdnom, restrict='oui')
556             # On ajoute dans le contexte de la macro les concepts nommes
557             # Ceci est indispensable pour les CO (macro) dans un INCLUDE
558             self.g_context[sdnom] = sd
559         else:
560             # La demande de nommage vient probablement d'une macro qui a mis
561             # le concept dans son contexte. On ne traite plus que le nommage (restrict="oui")
562             self.parent.NommerSdprod(sd, sdnom, restrict='oui')
563
564     def deleteConceptAfterEtape(self, etape, sd):
565         """
566             Met a jour les etapes de la MACRO  qui sont après etape suite a
567             la disparition du concept sd
568         """
569         # Cette methode est definie dans le noyau mais ne sert que pendant la phase de creation
570         # des etapes et des concepts. Il n'y a aucun traitement particulier a realiser
571         # Dans d'autres conditions, il faudrait surcharger cette methode.
572         return
573
574     def getCreated_sd(self):
575         """Retourne la liste des sd reellement produites par l'etape.
576         Si reuse est present, `self.sd` a ete creee avant, donc n'est pas dans
577         cette liste."""
578         sdprods = self.sdprods[:]
579         if not self.reuse and self.sd:
580             sdprods.append(self.sd)
581         return sdprods
582
583     def getLastConcept(self):
584         """Retourne le dernier concept produit dans la macro.
585         Peut-etre utile pour acceder au contenu 'fortran' dans une
586         clause 'except'."""
587         return self.g_context.get(self.last, None)
588
589     def accept(self, visitor):
590         """
591            Cette methode permet de parcourir l'arborescence des objets
592            en utilisant le pattern VISITEUR
593         """
594         visitor.visitMACRO_ETAPE(self)
595
596     def updateContext(self, d):
597         """
598            Met a jour le contexte contenu dans le dictionnaire d
599            Une MACRO_ETAPE peut ajouter plusieurs concepts dans le contexte
600            Une fonction enregistree dans op_init peut egalement modifier le contexte
601         """
602         if type(self.definition.op_init) == types.FunctionType:
603             self.definition.op_init(*(self, d))
604         if self.sd != None:
605             d[self.sd.nom] = self.sd
606         for co in self.sdprods:
607             d[co.nom] = co
608
609     def makeInclude(self, unite=None, fname=None):
610         """Inclut un fichier dont l'unite logique est `unite` ou de nom `fname`"""
611         if unite is not None:
612             warn("'unite' is deprecated, please use 'fname' instead",
613                  DeprecationWarning, stacklevel=2)
614             fname = 'fort.%s' % unite
615         if not fname:
616             return
617         f, text = self.getFile(fic_origine=self.parent.nom, fname=fname)
618         self.fichier_init = f
619         if f == None:
620             return
621         self.makeContexte(f, text)
622
623     def makePoursuite(self):
624         """Inclut un fichier poursuite"""
625         raise NotImplementedError('this method must be derivated (in Eficas)')
626
627     def makeContexte(self, f, text):
628         """
629             Interprete le texte fourni (text) issu du fichier f
630             dans le contexte du parent.
631             Cette methode est utile pour le fonctionnement des
632             INCLUDE
633         """
634         # on execute le texte fourni dans le contexte forme par
635         # le contexte de l etape pere (global au sens Python)
636         # et le contexte de l etape (local au sens Python)
637         code = compile(text, f, 'exec')
638         d = self.g_context = self.macro_const_context
639         globs = self.getGlobalContexte()
640         d.update(globs)
641         exec(code, globs, d)
642         # pour ne pas conserver des references sur tout
643         self.macro_const_context = {}
644
645     def getGlobalContexte(self):
646         """
647             Cette methode retourne le contexte global fourni
648             par le parent(self) a une etape fille (l'appelant) pour
649             realiser des evaluations de texte Python (INCLUDE,...)
650         """
651         # Le contexte global est forme par concatenation du contexte
652         # du parent de self et de celui de l'etape elle meme (self)
653         # Pour les concepts, cela ne doit rien changer. Mais pour les constantes,
654         # les valeurs de getContexteAvant sont moins recentes que dans
655         # getGlobalContexte. On prend donc la precaution de ne pas ecraser
656         # ce qui y est deja.
657         d = self.parent.getGlobalContexte()
658         d.update(self.g_context)
659         d.update([(k, v) for k, v in list(self.parent.getContexteAvant(self).items())
660                   if d.get(k) is None])
661         return d
662
663     def getContexteCourant(self, etape_fille_du_jdc=None):
664         """
665            Retourne le contexte tel qu'il est au moment de l'execution de
666            l'etape courante.
667         """
668         ctx = {}
669         # update car par ricochet on modifierait jdc.current_context
670         ctx.update(self.parent.getContexteCourant(self))
671         # on peut mettre None car toujours en PAR_LOT='NON', donc la dernière
672         ctx.update(self.getContexteAvant(None))
673         return ctx
674
675     def getConcept(self, nomsd):
676         """
677             Methode pour recuperer un concept a partir de son nom
678             dans le contexte du jdc connu avant l'execution de la macro courante.
679         """
680         # chercher dans self.getContexteAvant, puis si non trouve
681         # self.parent.getConcept est peut-etre plus performant
682         co = self.getContexteCourant().get(nomsd.strip(), None)
683         if not isinstance(co, ASSD):
684             co = None
685         return co
686
687     def getConceptByType(self, nomsd, typesd, etape=None):
688         """
689             Methode pour recuperer un concept a partir de son nom et de son type.
690             Il aura comme père 'etape' (ou la macro courante si etape est absente).
691         """
692         return self.parent.getConceptByType(nomsd, typesd, etape=etape or self)
693
694     def copy(self):
695         """ Methode qui retourne une copie de self non enregistree auprès du JDC
696             et sans sd
697             On surcharge la methode de ETAPE pour exprimer que les concepts crees
698             par la MACRO d'origine ne sont pas crees par la copie mais eventuellement
699             seulement utilises
700         """
701         etape = N_ETAPE.ETAPE.copy(self)
702         etape.sdprods = []
703         return etape
704
705     def copyIntern(self, etape):
706         """ Cette methode effectue la recopie des etapes internes d'une macro
707             passee en argument (etape)
708         """
709         self.etapes = []
710         self.index_etapes = {}
711         for etp in etape.etapes:
712             new_etp = etp.copy()
713             new_etp.copyReuse(etp)
714             new_etp.copySdnom(etp)
715             new_etp.reparent(self)
716             if etp.sd:
717                 new_sd = etp.sd.__class__(etape=new_etp)
718                 new_etp.sd = new_sd
719                 if etp.reuse:
720                     new_sd.setName(etp.sd.nom)
721                 else:
722                     self.NommerSdprod(new_sd, etp.sd.nom)
723             new_etp.copyIntern(etp)
724             self.etapes.append(new_etp)
725             self.index_etapes[new_etp] = len(self.etapes) - 1
726
727     def resetJdc(self, new_jdc):
728         """
729            Reinitialise l'etape avec un nouveau jdc parent new_jdc
730         """
731         if self.sd and self.reuse == None:
732             self.parent.NommerSdprod(self.sd, self.sd.nom)
733         for concept in self.sdprods:
734             self.parent.NommerSdprod(concept, concept.nom)
735
736     def reparent(self, parent):
737         """
738           Cette methode sert a reinitialiser la parente de l'objet
739         """
740         N_ETAPE.ETAPE.reparent(self, parent)
741         # on ne change pas la parente des concepts. On s'assure uniquement que
742         # le jdc en reference est le bon
743         for concept in self.sdprods:
744             concept.jdc = self.jdc
745         for e in self.etapes:
746             e.reparent(self)
747
748     def updateConstContext(self, d):
749         """
750            Met a jour le contexte des constantes pour l'evaluation de
751            formules dans la macro.
752         """
753         # Dans le jdc, const_context est mis a jour par execCompile
754         # Dans la macro, on n'a pas le code a compiler pour recupèrer les
755         # constantes locales a la macro. On demande donc explicitement de
756         # definir les constantes "locales".
757         self.macro_const_context.update(d)
758
759     def sdAccessible(self):
760         """On peut acceder aux "valeurs" (jeveux) des ASSD dans
761         les macro-commandes qui sont localement en PAR_LOT="NON"
762         sauf pour INCLUDE.
763         """
764         if CONTEXT.debug:
765             print((' `- MACRO sdAccessible :', self.nom))
766         return self.parent.sdAccessible() or not self.isInclude()