X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=Noyau%2FN_MACRO_ETAPE.py;h=a12147d15146e92bbba4365d4bbcab78bfe0a7bd;hb=9f4ff7ed9afd87c96e11d61e442e39e5511f60d1;hp=64d875aaff8e9cca55e0067adbe62266646f00f9;hpb=dbe648488708009d86ca09a606bd60b274fdd6f6;p=tools%2Feficas.git diff --git a/Noyau/N_MACRO_ETAPE.py b/Noyau/N_MACRO_ETAPE.py index 64d875aa..a12147d1 100644 --- a/Noyau/N_MACRO_ETAPE.py +++ b/Noyau/N_MACRO_ETAPE.py @@ -1,548 +1,766 @@ -#@ 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=utf-8 +# 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. -# -# -# ====================================================================== -""" - Ce module contient la classe MACRO_ETAPE qui sert à vérifier et à exécuter +# 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 a verifier et a executer une commande """ # Modules Python -import types,sys,string +from __future__ import absolute_import +from __future__ import print_function +try : + from builtins import str + from builtins import range +except : pass +import types +import sys import traceback +from warnings import warn # Modules EFICAS -import N_MCCOMPO -import N_ETAPE -from N_Exception import AsException -import N_utils -from N_utils import AsType +from . import N_MCCOMPO +from . import N_ETAPE +from .N_Exception import AsException +from . import N_utils +from .N_utils import AsType +from .N_CO import CO +from .N_ASSD import ASSD +from six.moves import range + class MACRO_ETAPE(N_ETAPE.ETAPE): - """ - - """ - nature = "COMMANDE" - 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={} - # 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 - # (nom : ASSD) déclarés dans la fonction sd_prod - self.Outputs={} - self.sd=None - self.actif=1 - self.sdprods=[] - self.make_register() - - def make_register(self): - """ - Initialise les attributs jdc, id, niveau et réalise les enregistrements - nécessaires - """ - if self.parent : - self.jdc = self.parent.get_jdc_root() - self.id=self.parent.register(self) - self.niveau=None - else: - self.jdc = self.parent =None - self.id=None - self.niveau=None - - def Build_sd(self,nom): - """ - Construit le concept produit de l'opérateur. Deux cas - 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. - - """ - if not self.isactif():return - self.sdnom=nom - try: - # 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 - # comme parent - self.set_current_step() - if self.parent: - sd= self.parent.create_sdprod(self,nom) - 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: - # On ne nomme le concept que dans le cas de non reutilisation - # d un concept - sd.nom=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) - except (EOFError,self.jdc.UserError): - # Le retablissement du step courant n'est pas strictement necessaire. On le fait pour des raisons de coherence - self.reset_current_step() - raise - except : - self.reset_current_step() - l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) - raise AsException("Etape ",self.nom,'ligne : ',self.appel[0], - 'fichier : ',self.appel[1]+'\n', - string.join(l)) - - self.Execute() - return sd - - def get_sd_prod(self): - """ - 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 - Si sd_prod == None le concept produit n existe pas on retourne None - Deux cas : - 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 - 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 - if type(self.definition.sd_prod) == types.FunctionType: - d=self.cree_dict_valeurs(self.mc_liste) + + """ + + """ + nature = "COMMANDE" + typeCO = CO + + def __init__(self, oper=None, reuse=None, args={}): + """ + Attributs : + - definition : objet portant les attributs de definition d'une etape + de type macro-commande. Il est initialise par + l'argument oper. + - reuse : indique le concept d'entree reutilise. Il se trouvera donc + en sortie si les conditions d'execution de l'operateur + l'autorise + - valeur : arguments d'entree de type mot-cle=valeur. Initialise + avec l'argument args. + """ + N_ETAPE.ETAPE.__init__(self, oper, reuse, args, niveau=5) + self.g_context = {} + # Contexte courant + self.current_context = {} + self.macro_const_context = {} + self.index_etape_courante = 0 + self.etapes = [] + self.index_etapes = {} + # Dans le cas d'une macro ecrite en Python, l'attribut Outputs est un + # dictionnaire qui contient les concepts produits de sortie + # (nom : ASSD) declares dans la fonction sd_prod + self.Outputs = {} + self.sdprods = [] + self.UserError = "UserError" + # permet de stocker le nom du dernier concept nomme dans la macro + self.last = None + + def make_register(self): + """ + Initialise les attributs jdc, id, niveau et realise les enregistrements + necessaires + """ + N_ETAPE.ETAPE.make_register(self) + if self.parent: + self.UserError = self.jdc.UserError + else: + self.UserError = "UserError" + + def Build_sd(self, nom): + """ + Construit le concept produit de l'operateur. Deux cas + peuvent se presenter : + + - le parent n'est pas defini. Dans ce cas, l'etape prend en charge + la creation et le nommage du concept. + + - le parent est defini. Dans ce cas, l'etape demande au parent la + creation et le nommage du concept. + + """ + self.sdnom = nom try: - # la sd_prod d'une macro a l'objet macro_etape lui meme en premier argument - # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute - # 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): - raise + # On positionne la macro self en tant que current_step pour que les + # etapes creees lors de l'appel a sd_prod et a op_init aient la macro + # comme parent + self.set_current_step() + if self.parent: + sd = self.parent.create_sdprod(self, nom) + if type(self.definition.op_init) == types.FunctionType: + 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 + # d un concept + sd.set_name(nom) + self.reset_current_step() + except AsException as e: + self.reset_current_step() + raise AsException("Etape ", self.nom, 'ligne : ', self.appel[0], + 'fichier : ', self.appel[1], e) + #except (EOFError, self.UserError): + except (EOFError): + # Le retablissement du step courant n'est pas strictement + # necessaire. On le fait pour des raisons de coherence + self.reset_current_step() + raise except: - if CONTEXT.debug: traceback.print_exc() - l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) - raise AsException("impossible d affecter un type au resultat\n",string.join(l[2:])) - - # on teste maintenant si la SD est réutilisée ou s'il faut la créer - if self.definition.reentrant != 'n' and self.reuse: - # Le concept produit est specifie reutilise (reuse=xxx). C'est une erreur mais non fatale. - # Elle sera traitee ulterieurement. - self.sd=self.reuse - else: - if sd_prod == None: - self.sd=None + self.reset_current_step() + l = traceback.format_exception( + sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]) + raise AsException("Etape ", self.nom, 'ligne : ', self.appel[0], + 'fichier : ', self.appel[1] + '\n', + ''.join(l)) + + self.Execute() + return sd + + def get_sd_prod(self): + """ + Retourne le concept resultat d'une macro etape + La difference avec une etape ou une proc-etape tient a ce que + le concept produit peut exister ou pas + + Si sd_prod == None le concept produit n existe pas on retourne None + + Deux cas : + - cas 1 : sd_prod n'est pas une fonction + il s'agit d'une sous classe de ASSD + on construit le sd a partir de cette classe + et on le retourne + - cas 2 : sd_prod est une fonction + on l'evalue avec les mots-cles de l'etape (mc_liste) + on construit le sd a 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: + # la sd_prod d'une macro a l'objet macro_etape lui meme en premier argument + # Comme sd_prod peut invoquer la methode type_sdprod qui ajoute + # les concepts produits dans self.sdprods, il faut le mettre a + # zero avant de l'appeler + self.sdprods = [] + sd_prod = sd_prod(*(self,), **d) + #except (EOFError, self.UserError): + except (EOFError): + raise + except Exception as exc: + if CONTEXT.debug: + traceback.print_exc() + raise AsException("impossible d affecter un type au resultat:", + str(exc)) + + # on teste maintenant si la SD est reutilisee ou s'il faut la creer + if self.definition.reentrant != 'n' and self.reuse: + # Le concept produit est specifie reutilise (reuse=xxx). C'est une erreur mais non fatale. + # Elle sera traitee ulterieurement. + self.sd = self.reuse 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. - return self.sd - - def get_type_produit(self,force=0): - """ - Retourne le type du concept résultat de l'étape et eventuellement type - les concepts produits "à droite" du signe égal (en entrée) - Deux cas : - 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 - 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 - """ - 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 sd_prod == None: + self.sd = None + 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. + # precaution + if self.sd is not None and not isinstance(self.sd, ASSD): + raise AsException(""" +Impossible de typer le resultat ! +Causes possibles : + Utilisateur : Soit la valeur fournie derrière "reuse" est incorrecte, + soit il y a une "," a la fin d'une commande precedente. + Developpeur : La fonction "sd_prod" retourne un type invalide.""") + return self.sd + + def get_type_produit(self, force=0): 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) + return self.get_type_produit_brut(force) except: - #traceback.print_exc() - return None - else: - sd_prod=self.definition.sd_prod - return sd_prod - - def get_contexte_avant(self,etape): - """ - Retourne le dictionnaire des concepts connus avant etape - pour les commandes internes a la macro - 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 - # 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 - return d - - def supprime(self): - """ - 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) - self.jdc=None - self.appel=None - if self.sd : self.sd.supprime() - for concept in self.sdprods: - concept.supprime() - for etape in self.etapes: - etape.supprime() - - 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 - Elle enregistre egalement les concepts produits (on fait l hypothese - 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 co.etape == None: - # le concept est libre - co.etape=self - co.__class__ = t - self.sdprods.append(co) - elif co.etape== self: - # le concept est produit par self - co.__class__ = t - self.sdprods.append(co) - 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 - # 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)) - co.etape=self - co.__class__ = t - self.sdprods.append(co) - 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 - self.sdprods.append(co) - else: - # le concept est produit par une autre étape - 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 - 0 = non - """ - if etape in self.etapes:return 1 - for etap in self.etapes: - if etap.issubstep(etape):return 1 - return 0 - - def register(self,etape): - """ - Enregistrement de etape dans le contexte de la macro : liste etapes - et demande d enregistrement global aupres du JDC - """ - self.etapes.append(etape) - 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) - """ - self.sds.append(sd) - 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. - """ - 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() - 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 - if not issubclass(sdprod,sd.__class__): - raise AsException("Le type du concept produit %s devrait etre une sur classe de %s" %(sd.__class__,sdprod)) - # La propriete du concept est transferee a l'etape avec le type attendu par l'étape - etape.sd=sd - sd.etape=etape - # 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 - # du concept associé à nomsd - etape.sdnom=sd.nom - 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 - # 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 - # 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 - # 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] == '_'): - etape.sdnom=sd.nom - else: - # On est dans le cas de la creation d'un nouveau concept - sd= etape.get_sd_prod() - if sd != None : - self.NommerSdprod(sd,nomsd) - 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('.') + # traceback.print_exc() + return None + + def get_type_produit_brut(self, force=0): + """ + Retourne le type du concept resultat de l'etape et eventuellement type + les concepts produits "a droite" du signe egal (en entree) + + Deux cas : + - 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 + - cas 2 : il s'agit d'une fonction + on l'evalue avec les mots-cles de l'etape (mc_liste) + et on retourne son resultat + """ + 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) + # Comme sd_prod peut invoquer la methode type_sdprod qui ajoute + # les concepts produits dans self.sdprods, il faut le mettre a zero + self.sdprods = [] + sd_prod = self.definition.sd_prod(*(self,), **d) 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') - - def delete_concept_after_etape(self,etape,sd): - """ - Met à jour les étapes de la MACRO qui sont après etape suite à - la disparition du concept sd - """ - # Cette methode est définie dans le noyau mais ne sert que pendant la phase de creation - # des etapes et des concepts. Il n'y a aucun traitement particulier à réaliser - # Dans d'autres conditions, il faudrait surcharger cette méthode. - return - - def accept(self,visitor): - """ - Cette methode permet de parcourir l'arborescence des objets - en utilisant le pattern VISITEUR - """ - visitor.visitMACRO_ETAPE(self) - - def update_context(self,d): - """ - Met à jour le contexte contenu dans le dictionnaire d - Une MACRO_ETAPE peut ajouter plusieurs concepts dans le contexte - Une fonction enregistree dans op_init peut egalement modifier le contexte - """ - if type(self.definition.op_init) == types.FunctionType: - apply(self.definition.op_init,(self,d)) - if self.sd != None:d[self.sd.nom]=self.sd - 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) - self.fichier_init = f - if f == None:return - self.make_contexte(f,text) - - 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) - - def make_contexte(self,f,text): - """ - Interprete le texte fourni (text) issu du fichier f - dans le contexte du parent. - Cette methode est utile pour le fonctionnement des - INCLUDE - """ - # 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 - - def get_global_contexte(self): - """ - Cette methode retourne le contexte global fourni - par le parent(self) a une etape fille (l'appelant) pour - realiser des evaluations de texte Python (INCLUDE,...) - """ - # 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) - return d - - def copy(self): - """ Méthode qui retourne une copie de self non enregistrée auprès du JDC - et sans sd - On surcharge la methode de ETAPE pour exprimer que les concepts crees - par la MACRO d'origine ne sont pas crees par la copie mais eventuellement - seulement utilises - """ - etape=N_ETAPE.ETAPE.copy(self) - etape.sdprods=[] - return etape - - - - - - + sd_prod = self.definition.sd_prod + return sd_prod + + def get_contexte_avant(self, etape): + """ + Retourne le dictionnaire des concepts connus avant etape + pour les commandes internes a la macro + On tient compte des commandes qui modifient le contexte + comme DETRUIRE ou les macros + """ + # L'etape courante pour laquelle le contexte a ete calcule est + # memorisee dans self.index_etape_courante + # 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()] + for nom in sd_names: + try: + del d[nom] + except KeyError: + pass + # Exemple avec INCLUDE_MATERIAU appele 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): + """ + Methode qui supprime toutes les references arrières afin que + l'objet puisse etre correctement detruit par le garbage collector + """ + N_MCCOMPO.MCCOMPO.supprime(self) + self.jdc = None + self.appel = None + if self.sd: + self.sd.supprime() + for concept in self.sdprods: + concept.supprime() + for etape in self.etapes: + etape.supprime() + + def clean(self, netapes): + """Nettoie les `netapes` dernières etapes de la liste des etapes.""" + if self.jdc.hist_etape: + return + for i in range(netapes): + e = self.etapes.pop() + jdc = e.jdc + parent = e.parent + e.supprime() + e.parent = parent + e.jdc = jdc + 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 + 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 a self + + Elle enregistre egalement les concepts produits (on fait l hypothese + que la liste sdprods a ete correctement initialisee, vide probablement) + """ + 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: + # 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 + co.change_type(t) + self.sdprods.append(co) + + 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 etre creer par une autre macro ? + # On verifie juste que c'est un vrai CO non deja type + # if co.etape == co._etape: + if co.is_typco() == 1: + # Le concept a ete cree par la macro (self) + # On peut changer son type + co.change_type(t) + else: + # Le concept a ete cree 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) + + elif co.etape == self.parent: + # 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 coherente avec les règles de + # verification des mots-cles + 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 + # On ne change pas le type car il respecte la condition isinstance(co,t) + # co.__class__ = t + self.sdprods.append(co) + + elif self.issubstep(co.etape): + # Cas 4 : Le concept est propriete 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) + else: + # Cas 5 : le concept est produit par une autre etape + # On ne fait rien + 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 + 0 = non + """ + if etape in self.etapes: + return 1 + for etap in self.etapes: + if etap.issubstep(etape): + return 1 + return 0 + + def register(self, etape): + """ + Enregistrement de etape dans le contexte de la macro : liste etapes + 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): + """ + Methode appelee dans l __init__ d un ASSD a sa creation pour + s enregistrer (reserve aux ASSD crees au sein d'une MACRO) + """ + return self.jdc.o_register(sd) + + def create_sdprod(self, etape, nomsd): + """ + Cette methode doit fabriquer le concept produit retourne + par l'etape etape et le nommer. + + Elle est appelee a 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 realise par le contexte superieur (etape.parent) + car dans certains cas, le concept ne doit pas etre fabrique mais + l'etape doit simplement utiliser un concept preexistant. + - Cas 1 : etape.reuse != None : le concept est reutilise + - Cas 2 : l'etape appartient a une macro qui a declare un concept + de sortie qui doit etre produit par cette etape. + """ + if nomsd in self.Outputs: + # Il s'agit d'un concept de sortie de la macro. Il ne faut pas le creer + # 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 ulterieure. + 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 coherente avec les règles de + # verification des mots-cles + if not issubclass(sdprod, sd.__class__): + raise AsException( + "Le type du concept produit %s devrait etre une sur classe de %s" % (sd.__class__, sdprod)) + # La propriete du concept est transferee a l'etape avec le type + # attendu par l'etape + etape.sd = sd + sd.etape = etape + if self.reuse == sd and etape.reuse != sd \ + and getattr(sd, "executed", 0) == 1: # n'a pas ete pas detruit + raise AsException("Le concept '%s' est reentrant dans la macro-commande %s. " + "Il devrait donc l'etre 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 force egalement le nom stocke dans l'attribut sdnom : on lui donne le nom + # du concept associe a 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 + # 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, + # 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 + # 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] == '_'): + etape.sdnom = sd.nom + else: + # On est dans le cas de la creation d'un nouveau concept + sd = etape.get_sd_prod() + if sd != None: + self.NommerSdprod(sd, nomsd) + 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 considère que l'espace de nom est unique et gere par le JDC. + Si le nom est deja utilise, 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. + if sdnom in self.Outputs : + # 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 deja 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 detruits + # nom commençant par _ : il s'agit de concepts intermediaires + # qui seront gardes + 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 a voir, creation de CO() dans CALC_ESSAI (sdls139a) + if not sd.is_typco(): + raise AsException( + "Resultat non declare 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 + 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') + + def delete_concept_after_etape(self, etape, sd): + """ + Met a jour les etapes de la MACRO qui sont après etape suite a + la disparition du concept sd + """ + # Cette methode est definie dans le noyau mais ne sert que pendant la phase de creation + # des etapes et des concepts. Il n'y a aucun traitement particulier a realiser + # Dans d'autres conditions, il faudrait surcharger cette methode. + return + + def get_created_sd(self): + """Retourne la liste des sd reellement produites par l'etape. + Si reuse est present, `self.sd` a ete creee 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-etre utile pour acceder 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 + en utilisant le pattern VISITEUR + """ + visitor.visitMACRO_ETAPE(self) + + def update_context(self, d): + """ + Met a jour le contexte contenu dans le dictionnaire d + Une MACRO_ETAPE peut ajouter plusieurs concepts dans le contexte + Une fonction enregistree dans op_init peut egalement modifier le contexte + """ + if type(self.definition.op_init) == types.FunctionType: + self.definition.op_init(*(self, d)) + if self.sd != None: + d[self.sd.nom] = self.sd + for co in self.sdprods: + d[co.nom] = co + + 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 + if f == None: + return + self.make_contexte(f, text) + + def make_poursuite(self): + """Inclut un fichier poursuite""" + raise NotImplementedError('this method must be derivated (in Eficas)') + + def make_contexte(self, f, text): + """ + Interprete le texte fourni (text) issu du fichier f + dans le contexte du parent. + Cette methode est utile pour le fonctionnement des + INCLUDE + """ + # 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 = self.macro_const_context + globs = self.get_global_contexte() + d.update(globs) + exec(code, globs, d) + # pour ne pas conserver des references sur tout + self.macro_const_context = {} + + def get_global_contexte(self): + """ + Cette methode retourne le contexte global fourni + par le parent(self) a une etape fille (l'appelant) pour + realiser des evaluations de texte Python (INCLUDE,...) + """ + # Le contexte global est forme par concatenation du contexte + # du parent de self et de celui de l'etape elle meme (self) + # Pour les concepts, cela ne doit rien changer. Mais pour les constantes, + # les valeurs de get_contexte_avant sont moins recentes que dans + # get_global_contexte. On prend donc la precaution de ne pas ecraser + # ce qui y est deja. + d = self.parent.get_global_contexte() + d.update(self.g_context) + d.update([(k, v) for k, v in list(self.parent.get_contexte_avant(self).items()) + if d.get(k) is None]) + return d + + def get_contexte_courant(self, etape_fille_du_jdc=None): + """ + Retourne le contexte tel qu'il est au moment de l'execution de + l'etape 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): + """ + Methode pour recuperer un concept a partir de son nom + dans le contexte du jdc connu avant l'execution 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): + """ + Methode pour recuperer un concept a 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): + """ Methode qui retourne une copie de self non enregistree auprès du JDC + et sans sd + On surcharge la methode de ETAPE pour exprimer que les concepts crees + par la MACRO d'origine ne sont pas crees par la copie mais eventuellement + seulement utilises + """ + etape = N_ETAPE.ETAPE.copy(self) + etape.sdprods = [] + return etape + + def copy_intern(self, etape): + """ Cette methode effectue la recopie des etapes internes d'une macro + passee 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 parente des concepts. On s'assure uniquement que + # le jdc en reference 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 a jour le contexte des constantes pour l'evaluation de + formules dans la macro. + """ + # Dans le jdc, const_context est mis a jour par exec_compile + # Dans la macro, on n'a pas le code a compiler pour recupèrer les + # constantes locales a la macro. On demande donc explicitement de + # definir 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()