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