Salome HOME
fusion Telemac
[tools/eficas.git] / Noyau / nommage.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 sert à nommer les concepts produits par les commandes.
23    Le nom du concept est obtenu en appelant la fonction getNomConceptResultat
24    du module avec le nom de la commande en argument.
25
26    Cette fonction parcourt le source dans lequel la commande se trouve, parse le
27    fichier et retrouve le nom du concept qui se trouve à gauche du signe = précédant
28    le nom de la commande.
29
30    Cette fonction utilise la fonction cur_frame du module N_utils qui retourne la frame
31    d'exécution Python située 2 niveaux au-dessus. C'est à partir de cette frame que
32    l'on retrouve le fichier source et le numéro de ligne où se trouve l'appel à la commande.
33
34 """
35
36 # Modules Python
37 from __future__ import absolute_import
38 try :
39     from builtins import str
40 except :
41     pass
42 import re
43 import linecache
44 import sys
45 from functools import partial
46
47 # Modules EFICAS
48 from . import N_utils
49 from .strfunc import getEncoding
50
51 regex1 = '=?\s*%s\s*\('
52 # commentaire standard precede d'un nombre quelconque de blancs (pas
53 # multiligne)
54 pattern_comment = re.compile(r"^\s*#.*")
55
56
57 def _getNomConceptResultat(ope, level=2):
58     """
59        Cette fonction recherche dans la pile des appels, l'appel à la commande
60        qui doit etre situé à 2 niveaux au-dessus (cur_frame(2)).
61        On retrouve d'abord la frame d'exécution f. Puis le numéro de la ligne
62        dans le source f.f_lineno et le nom du fichier source (f.f_code.co_filename).
63        A partir de là, on récupère la ligne de source avec linecache.getline
64        et on vérifie que cette ligne correspond véritablement à l'appel.
65
66        En effet, lorsque les commandes tiennent sur plusieurs lignes, on retrouve
67        la dernière ligne. Il faut donc remonter dans le source jusqu'à la première
68        ligne.
69
70        Enfin la fonction evalnom forme un nom acceptable lorsque le concept est un
71        élément d'une liste, par exemple.
72
73     """
74     f = N_utils.cur_frame(level)
75     lineno = f.f_lineno     # XXX Too bad if -O is used
76     # lineno = f_lineno(f)  # Ne marche pas toujours
77     co = f.f_code
78     if sys.version_info >= (3,0) :
79         filename = co.co_filename
80     else :
81         import six
82         filename = six.text_type(co.co_filename, getEncoding())
83     name = co.co_name
84     # pattern pour identifier le debut de la commande
85     pattern_oper = re.compile(regex1 % ope)
86
87     list = []
88     while lineno > 0:
89         line = linecache.getline(filename, lineno)
90         lineno = lineno - 1
91         if pattern_comment.match(line):
92             continue
93         list.append(line)
94         if pattern_oper.search(line):
95             l = pattern_oper.split(line)
96             list.reverse()
97             # On suppose que le concept resultat a bien ete
98             # isole en tete de la ligne de source
99             m = evalnom(l[0].strip(), f.f_locals)
100             # print "NOMS ",m
101             if m != []:
102                 return m[-1]
103             else:
104                 return ''
105     # print "appel inconnu"
106     return ""
107
108
109 def evalnom(text, d):
110     """
111      Retourne un nom pour le concept resultat identifie par text
112      Pour obtenir ce nom il y a plusieurs possibilites :
113       1. text est un identificateur python c'est le nom du concept
114       2. text est un element d'une liste on construit le nom en
115         evaluant la partie indice dans le contexte de l'appelant d
116     """
117     l = re.split('([\[\]]+)', text)
118     if l[-1] == '':
119         l = l[:-1]
120     lll = []
121     i = 0
122     while i < len(l):
123         s = l[i]
124         ll = re.split('[ ,]+', s)
125         if ll[0] == '':
126             ll = ll[1:]
127         if len(ll) == 1:
128             id0 = ll[0]
129         else:
130             lll = lll + ll[0:-1]
131             id0 = ll[-1]
132         if i + 1 < len(l) and l[i + 1] == '[':  # le nom est suivi d un subscript
133             sub = l[i + 2]
134             nom = id0 + '_' + str(eval(sub, d))
135             i = i + 4
136         else:
137             nom = id0
138             i = i + 1
139         lll.append(nom)
140     return lll
141
142
143 def f_lineno(f):
144     """
145        Calcule le numero de ligne courant
146        Devrait marcher meme avec -O
147        Semble ne pas marcher en présence de tuples longs
148     """
149     c = f.f_code
150     if not hasattr(c, 'co_lnotab'):
151         return f.f_lineno
152     tab = c.co_lnotab
153     line = c.co_firstlineno
154     stopat = f.f_lasti
155     addr = 0
156     for i in range(0, len(tab), 2):
157         addr = addr + ord(tab[i])
158         if addr > stopat:
159             break
160         line = line + ord(tab[i + 1])
161     return line
162
163
164 class NamingSystem(N_utils.Singleton):
165
166     """Cette classe définit un système de nommage dynamique des concepts."""
167     _singleton_id = 'nommage.NamingSystem'
168
169     def __init__(self):
170         """Initialisation"""
171         self.native = _getNomConceptResultat
172         self.useGlobalNaming()
173
174     def useNamingFunction(self, function):
175         """Utilise une fonction particulière de nommage."""
176         self.naming_func = function
177
178     def useGlobalNaming(self):
179         """Utilise la fonction native de nommage."""
180         self.naming_func = partial(self.native, level=3)
181
182     def __call__(self, *args):
183         """Appel à la fonction de nommage."""
184         return self.naming_func(*args)
185
186 getNomConceptResultat = NamingSystem()