1# -*- coding: utf-8 -*- 2 3# ============================================================================== 4# COPYRIGHT (C) 1991 - 2015 EDF R&D WWW.CODE-ASTER.ORG 5# THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY 6# IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY 7# THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR 8# (AT YOUR OPTION) ANY LATER VERSION. 9# 10# THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT 11# WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF 12# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU 13# GENERAL PUBLIC LICENSE FOR MORE DETAILS. 14# 15# YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE 16# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER, 17# 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE. 18# ============================================================================== 19 20""" 21Tools for developpers : 22 - search messages 23""" 24 25 26import os 27import os.path as osp 28import re 29import pickle 30import time 31from glob import glob 32 33from asrun.common.i18n import _ 34from asrun.mystring import ufmt 35from asrun.config import AsterConfig, build_config_of_version 36from asrun.build import AsterBuild, unigest2dict 37from asrun.progress import Progress 38 39 40class CataMessageError(Exception): 41 """Error with a catalog of messages.""" 42 43 def __init__(self, fcata, reason): 44 self.reason = reason 45 self.fcata = fcata 46 47 def __str__(self): 48 return ufmt(_("%s: %s, error: %s"), 49 self.__class__.__name__, self.fcata, self.reason) 50 51 52class MessageError(Exception): 53 """Error on a message.""" 54 55 def __init__(self, msgid, reason): 56 self.reason = reason 57 self.msgid = msgid 58 59 def __str__(self): 60 return "%s: %s, error: %s" \ 61 % (self.__class__.__name__, self.msgid, self.reason) 62 63 64class MESSAGE_MANAGER(object): 65 """Classe pour récupérer des informations sur le catalogue de messages. 66 """ 67 def __init__(self, repref, 68 fort=('bibfor', 'bibf90'), 69 pyt='bibpyt', 70 capy='catapy', 71 cache_dict=None, verbose=True, force=False, 72 ignore_comments=True, debug=False, 73 surch_fort=[], surch_pyt=[], unig=None): 74 """Initialisations.""" 75 if type(fort) not in (list, tuple): 76 fort = [fort,] 77 if type(surch_fort) not in (list, tuple): 78 surch_fort = [surch_fort,] 79 if type(surch_pyt) not in (list, tuple): 80 surch_pyt = [surch_pyt,] 81 self.repref = repref 82 self.fort = [osp.join(repref, rep) for rep in fort] 83 self.pyt = osp.join(repref, pyt) 84 self.capy = osp.join(repref, capy) 85 self.surch_fort = [osp.abspath(d) for d in surch_fort] 86 self.surch_pyt = [osp.abspath(d) for d in surch_pyt] 87 self.verbose = verbose 88 self.debug = debug 89 self.force = force 90 self.message_dir = 'Messages' 91 self.cache_dict = cache_dict or osp.join('/tmp', 'messages_dicts.pick') 92 self.ignore_comments = ignore_comments 93 self.wrk_fort = osp.join(os.getcwd(), 'F') 94 self.wrk_pyt = osp.join(os.getcwd(), 'PY') 95 96 self.unig = unig 97 # init AsterConfig, AsterBuild objects 98 fconf = osp.join(self.repref, 'config.txt') 99 self.conf_obj = AsterConfig(fconf, run=None) 100 self.build_obj = AsterBuild(run=None, conf=self.conf_obj) 101 if unig: 102 # use with_fordepla=False not to mark moved files as removed 103 self.unig = unigest2dict(unig, self.conf_obj, with_fordepla=True) 104 105 if self.verbose: 106 print('Repertoires :\n REF=%s\n FORTRAN=%s\n PYTHON=%s\n CAPY=%s' \ 107 % (self.repref, self.fort, self.pyt, self.capy)) 108 109 self.l_cata = self._get_list_cata() 110 self.l_src, self.l_pyt = self._get_list_src() 111# self._build_regexp() 112 113 def read_cata(self): 114 """Read all catalogues""" 115 self._build_dict() 116 self.print_stats() 117 118 119 def _filename_cata(self, cata): 120 """Retourne le nom du fichier associé à un catalogue.""" 121 name = osp.join(self.pyt, self.message_dir, cata + '.py') 122 return name 123 124 125 def _id2filename(self, msgid): 126 """Retourne modelisa5 à partir de MODELISA5_46""" 127 mat = re.search('([A-Z]+[0-9]*)_([0-9]+)', msgid) 128 if mat == None: 129 raise MessageError(msgid, _('invalid message id')) 130 cata, num = mat.groups() 131 return self._filename_cata(cata.lower()), int(num) 132 133 134 def _filename2id(self, fcata, num): 135 """Retourne MODELISA5_46 à partir de modelisa5""" 136 return '%s_%d' % (osp.splitext(osp.basename(fcata))[0].upper(), num) 137 138 139 def makedirs(self): 140 """Crée les répertoires pour les fichiers modifiés 141 """ 142 for d in (self.wrk_fort, self.wrk_pyt): 143 if not osp.isdir(d): 144 os.makedirs(d) 145 146 147 def _get_list_cata(self): 148 """Retourne la liste des catalogues de messages. 149 """ 150 re_mess = re.compile('#@.*Messages') 151 s_cata = set(glob(osp.join(self.pyt, self.message_dir, '*.py'))) 152 s_suppr = set() 153 l_surch = [] 154 # ajouter la surcharge et retirer le catalogue d'origine 155 for dsrc in self.surch_pyt: 156 for f in glob(osp.join(dsrc, '*.py')) + glob(osp.join(dsrc, '*', '*.py')): 157 with open(f, 'r') as f: 158 txt = f.read() 159 if re_mess.search(txt): 160 l_surch.append(osp.abspath(f)) 161 fsup = osp.join(self.pyt, self.message_dir, osp.basename(f)) 162 if osp.exists(fsup): 163 s_suppr.add(fsup) 164 s_cata.update(l_surch) 165 if len(l_surch) > 0: 166 print('%5d catalogues en surcharge : ' % len(l_surch)) 167 print(os.linesep.join(l_surch)) 168 169 s_cata = set([osp.abspath(f) for f in s_cata \ 170 if not osp.basename(f) in ['__init__.py', 'context_info.py', 'fermetur.py']]) 171 l_suppr = [] 172 if self.unig and len(self.unig['py']) > 0: 173 l_suppr = [osp.abspath(osp.join(self.repref, f)) \ 174 for f in self.unig['py'] if f.find(self.message_dir) > -1] 175 s_suppr.update(l_suppr) 176 l_suppr = list(s_suppr) 177 l_suppr.sort() 178 if len(l_suppr) > 0: 179 print('%5d catalogue(s) supprime(s) :' % len(l_suppr)) 180 if self.verbose: print(os.linesep.join(l_suppr)) 181 l_cata = list(s_cata.difference(s_suppr)) 182 l_cata.sort() 183 return l_cata 184 185 186 def check_cata(self): 187 """Vérifie les catalogues. 188 """ 189 if self.verbose: 190 print('%5d catalogues dans %s + %s' \ 191 % (len(self.l_cata), self.pyt, self.surch_pyt)) 192 all_msg = [] 193 error_occurs = False 194 for f in self.l_cata: 195 try: 196 cata = CATA(f) 197 cata.check() 198 all_msg.extend([self._filename2id(f, i) for i in cata]) 199 if self.verbose: 200 print(cata) 201 except CataMessageError as msg: 202 error_occurs = True 203 print(msg) 204 self.l_cata.remove(f) 205 if error_occurs: 206 raise CataMessageError(_("global"), _("errors occurred!")) 207 all_msg = set(all_msg) 208 209 # messages jamais appelés 210 used = set(self.d_msg_call.keys()) 211 unused = list(all_msg.difference(used)) 212 unused.sort() 213 214 union = used.union(all_msg) 215 not_found = list(used.difference(all_msg)) 216 not_found.sort() 217 if self.verbose: 218 print('%6d messages dans les catalogues' % len(all_msg)) 219 print('dont %6d messages appeles presents dans les catalogues' % (len(used) - len(not_found))) 220 print(' et %6d messages inutilises' % len(unused)) 221 print(' + %6d messages appeles absents des catalogues' % len(not_found)) 222 #print '%6d messages total (union pour verif)' % len(union) 223 return unused, not_found 224 225 226 def _build_regexp(self): 227 """Construit la liste des expressions régulières pour la recherche des messages. 228 """ 229 l_regexp = [] 230 for cata in self.l_cata: 231 cata = osp.splitext(osp.basename(cata))[0] 232 l_regexp.append('%s_[0-9]+' % cata.upper()) 233 self.regexp = re.compile('|'.join(l_regexp)) 234 235 236 def _get_list_src(self): 237 """Retourne la liste des routines fortran et python. 238 """ 239 l_f = self._get_list_fort() 240 l_pyt = self._get_list_python() 241 if self.verbose: 242 print('%5d routines fortran dans %s + %s' % (len(l_f), self.fort, self.surch_fort)) 243 print('%5d modules python dans %s + %s' % (len(l_pyt), self.pyt, self.surch_pyt)) 244 return l_f, l_pyt 245 246 247 def _get_list_fort(self): 248 """Retourne la liste des fichiers sources fortran/fortran90.""" 249 s_f = set() 250 for dsrc in self.fort: 251 s_f.update(glob(osp.join(dsrc, '*.f'))) 252 s_f.update(glob(osp.join(dsrc, '*', '*.f'))) 253 s_f.update(glob(osp.join(dsrc, '*.F'))) 254 s_f.update(glob(osp.join(dsrc, '*', '*.F'))) 255 s_f.update(glob(osp.join(dsrc, '*.F90'))) 256 s_f.update(glob(osp.join(dsrc, '*', '*.F90'))) 257 if self.build_obj.support('waf'): 258 l_f = [osp.abspath(f) for f in s_f] 259 l_f.sort() 260 return l_f 261 d_f = {} 262 for f in s_f: 263 assert d_f.get(osp.basename(f)) is None, 'ERROR : %s (old : %s)' % (f, d_f[osp.basename(f)]) 264 d_f[osp.basename(f)] = f 265 # surcharge 266 s_surch = set() 267 for dsrc in self.surch_fort: 268 s_surch.update(glob(osp.join(dsrc, '*.f'))) 269 s_surch.update(glob(osp.join(dsrc, '*', '*.f'))) 270 s_surch.update(glob(osp.join(dsrc, '*.F'))) 271 s_surch.update(glob(osp.join(dsrc, '*', '*.F'))) 272 if len(s_surch) > 0: 273 l_surch = list(s_surch) 274 l_surch.sort() 275 print('%5d sources en surcharge : ' % len(l_surch)) 276 print(os.linesep.join(l_surch)) 277 # retirer des sources originaux ceux de la surcharge... 278 s_suppr = set() 279 for f in s_surch: 280 fexist = d_f.get(osp.basename(f)) 281 if fexist: 282 s_suppr.add(fexist) 283 if self.verbose: print('suppression :', fexist) 284 # ... et ceux de l'unigest 285 if self.unig: 286 iunig = 0 287 for f in self.unig['f'] + self.unig['f90']: 288 iunig += 1 289 s_suppr.add(d_f.get(osp.basename(f), '')) 290 if self.verbose: print('suppression :', f) 291 if iunig > 0: 292 print('%5d source(s) supprime(s).' % iunig) 293 294 s_f.difference_update(s_suppr) 295 s_f.update(s_surch) 296 l_f = [osp.abspath(f) for f in s_f] 297 l_f.sort() 298 return l_f 299 300 301 def _get_list_python(self): 302 """Retourne la liste des fichiers python 303 """ 304 s_pyt = set() 305 s_pyt.update(glob(osp.join(self.pyt, '*.py'))) 306 s_pyt.update(glob(osp.join(self.pyt, '*', '*.py'))) 307 s_pyt.update(glob(osp.join(self.capy, '*.capy'))) 308 s_pyt.update(glob(osp.join(self.capy, '*', '*.capy'))) 309 if self.build_obj.support('waf'): 310 l_pyt = [osp.abspath(f) for f in s_pyt] 311 l_pyt.sort() 312 return l_pyt 313 d_py = {} 314 for f in s_pyt: 315 typ = osp.splitext(f)[-1][1:] 316 key = self.build_obj.GetCModif(typ, f) 317 assert d_py.get(key) is None, 'ERROR : %s (old : %s)' % (key, d_py[key]) 318 d_py[key] = f 319 # surcharge 320 s_surch = set() 321 for dsrc in self.surch_pyt: 322 s_surch.update(glob(osp.join(dsrc, '*.py'))) 323 s_surch.update(glob(osp.join(dsrc, '*', '*.py'))) 324 s_surch.update(glob(osp.join(dsrc, '*.capy'))) 325 s_surch.update(glob(osp.join(dsrc, '*', '*.capy'))) 326 if len(s_surch) > 0: 327 l_surch = list(s_surch) 328 l_surch.sort() 329 print('%5d module(s) python en surcharge : ' % len(l_surch)) 330 print(os.linesep.join(l_surch)) 331 # retirer des sources originaux ceux de la surcharge... 332 s_suppr = set() 333 for f in s_surch: 334 typ = osp.splitext(f)[-1][1:] 335 key = self.build_obj.GetCModif(typ, f) 336 fexist = d_py.get(key) 337 if fexist: 338 s_suppr.add(fexist) 339 if self.verbose: print('suppression :', fexist) 340 # ... et ceux de l'unigest 341 if self.unig: 342 iunig = 0 343 for typ in ('py', 'capy'): 344 for f in self.unig[typ]: 345 iunig += 1 346 fabs = osp.abspath(osp.join(self.repref, f)) 347 key = self.build_obj.GetCModif(typ, fabs) 348 s_suppr.add(d_py.get(key, '')) 349 if self.verbose: print('suppression :', fabs) 350 if iunig > 0: 351 print('%5d module(s) python supprime(s).' % iunig) 352 353 s_pyt.difference_update(s_suppr) 354 s_pyt.update(s_surch) 355 l_pyt = [osp.abspath(f) for f in s_pyt] 356 l_pyt.sort() 357 return l_pyt 358 359 360 def search_message(self, fich): 361 """Retourne les messages utilisés dans 'fich'. 362 """ 363 try: 364 with open(fich, 'r') as f: 365 txt = f.read() 366 except IOError as msg: 367 print(_('Error with file %s : %s') % (fich, msg)) 368 return [] 369 ext = osp.splitext(fich)[-1] 370 txt = clean_source(txt, ext, ignore_comments=self.ignore_comments, wrap=True) 371 372 if osp.splitext(fich)[-1] not in ('.py', '.capy'): 373 expr = (r'CALL\s+(U2MES.|UTEXC[MP]+|UTPRIN|UTMESS|UTMESS_CORE)\s*' 374 r'\(([^,]*?), *[\'\"]+(.*?)[\'\"]+ *[,\)]+') 375 else: 376 expr = (r'[\s\.:]+(UTMESS|GetText)\s*\(([^,]*?), *' 377 r'[\'\"]+(.*?)[\'\"]+ *[,\)]+') 378 all_msg = re.findall(expr, txt, flags=re.I) 379 l_msg = [] 380 for found in all_msg: 381 sub, typ, msg = found 382 if msg.startswith('FERMETUR_'): 383 pass 384 elif re.search('^[A-Z0-9]+_[0-9]+$', msg) is None: 385 print("Invalid message id ('%s') in file %s (%s)" % (msg, fich, sub)) 386 else: 387 mattyp = re.search(r'[\'\" ]+(.*?)[\+\'\" ]+', typ) 388 # variables and numbers (for exception) are allowed 389 if mattyp is not None and \ 390 mattyp.group(1) not in ('', 'A', 'I', 'E', 'S', 'F', 'M', 'D'): 391 # may be '' for example STY//'+' 392 print("Invalid message type (%s) for message '%s' in file %s" \ 393 % (mattyp.group(1), msg, fich)) 394 print("type = %s" % typ) 395 else: 396 l_msg.append(msg) 397# l_msg = self.regexp.findall(txt) 398 399 # verif 400 l_res = [] 401 for msg in l_msg: 402 spl = msg.split('_') 403 assert len(spl) == 2, 'ERROR invalid : %s' % msg 404 msg = '%s_%d' % (spl[0], int(spl[1])) 405 l_res.append(msg) 406 407 return l_res 408 409 410 def _build_dict(self): 411 """Construit les dictionnaires : 412 fichier source : liste des messsages appelés 413 message : liste des fichiers appelant ce message 414 """ 415 # est-ce dans le cache ? 416 if not self.force and osp.isfile(self.cache_dict) \ 417 and os.stat(self.cache_dict).st_size > 0: 418 if self.verbose: 419 print('Load dicts from cache (%s)...' % self.cache_dict) 420 with open(self.cache_dict, 'rb') as pick: 421 self.d_msg_used = pickle.load(pick) 422 self.d_msg_call = pickle.load(pick) 423 424 else: 425 self.d_msg_used = {} 426 self.d_msg_call = {} 427 l_all = self.l_src + self.l_pyt 428 if self.verbose: 429 p = Progress(maxi=len(l_all), format='%5.1f %%', 430 msg='Analyse des sources... ') 431 for i, f in enumerate(l_all): 432 if self.verbose: 433 p.Update(i) 434# key = f.replace(self.repref, '') 435 key = re.sub('^%s/*', '', f) 436 lm = self.search_message(f) 437 if len(lm) > 0: 438 self.d_msg_used[key] = lm 439 for msg in lm: 440 self.d_msg_call[msg] = self.d_msg_call.get(msg, []) + [key,] 441 442 if self.verbose: 443 p.End() 444 #pprint.pprint(self.d_msg_used) 445 446 with open(self.cache_dict, 'wb') as pick: 447 pickle.dump(self.d_msg_used, pick) 448 pickle.dump(self.d_msg_call, pick) 449 450 451 def print_stats(self): 452 """Affiche les stats sur les données lues/construites 453 """ 454 if self.verbose: 455 print('%6d messages appelés dans les sources' % len(list(self.d_msg_call.keys()))) 456 print('%6d fichiers sources appellent le catalogue de messages' % len(list(self.d_msg_used.keys()))) 457 458 459 def move(self, oldid, dest, reuse_hole=True): 460 """Déplace le message 'oldid' dans le catalogue 'dest'. 461 """ 462 if self.verbose: 463 print('--- moving "%s" into "%s"' % (oldid, dest)) 464 # catalogue objects 465 old_f, old_num = self._id2filename(oldid) 466 new_f = self._filename_cata(dest.lower()) 467 468 # have these catalogues been already changed ? 469 fmod = osp.join(self.wrk_pyt, osp.basename(old_f)) 470 if osp.isfile(fmod): 471 print('from %s' % fmod) 472 old_f = fmod 473 fmod = osp.join(self.wrk_pyt, osp.basename(new_f)) 474 if osp.isfile(fmod): 475 print('from %s' % fmod) 476 new_f = fmod 477 478 old_cata = CATA(old_f) 479 new_cata = CATA(new_f) 480 if self.verbose: 481 print(old_cata) 482 print(new_cata) 483 new_num = new_cata.get_new_id(reuse_hole) 484 if new_num < 0: 485 raise CataMessageError(new_f, _('no message id available in this catalog')) 486 newid = self._filename2id(new_f, new_num) 487 488 # check message existence 489 if old_cata[old_num] == None: 490 raise MessageError(oldid, _('unknown message')) 491 492 new_cata[new_num] = old_cata[old_num] 493 del old_cata[old_num] 494 495 # write modified catalogues 496 self.makedirs() 497 fout = osp.join(self.wrk_pyt, osp.basename(old_f)) 498 content = old_cata.get_content() 499 with open(fout, 'w') as f: 500 f.write(content) 501 fout = osp.join(self.wrk_pyt, osp.basename(new_f)) 502 content = new_cata.get_content() 503 with open(fout, 'w') as f: 504 f.write(content) 505 print('Nouveau fichier : %s' % fout) 506 507 # modify source using 'oldid' message 508 l_src = self.d_msg_call.get(oldid, []) 509 for f in l_src: 510 ext = osp.splitext(f)[-1] 511 rdest = self.wrk_fort 512 if ext == '.py': 513 rdest = self.wrk_pyt 514 # already changed ? 515 fmod = osp.join(rdest, osp.basename(f)) 516 if osp.isfile(fmod): 517 print('from %s' % fmod) 518 f = fmod 519 with open(osp.join(self.repref, f), 'r') as f: 520 txt = f.read() 521 new = re.sub('%s([\'\"]+)' % oldid, newid + r'\1', txt) 522 fout = osp.join(rdest, osp.basename(f)) 523 with open(fout, 'w') as f: 524 f.write(new) 525 print('Nouveau fichier : %s' % fout) 526 527 528 def who_use(self, msg): 529 """Qui utilise le message 'msg' ? 530 """ 531 return tuple(self.d_msg_call.get(msg.upper(), [])) 532 533 534 def get_key(self, sub): 535 """Retourne la clé dans d_msg_used correspondant à 'sub'. 536 Seule la première est retournée si jamais il y en avait plusieurs. 537 """ 538 l_allsub = list(self.d_msg_used.keys()) 539 l_sub = [f for f in l_allsub if f.split(os.sep)[-1] == sub] 540 if len(l_sub) > 1: 541 print('Plusieurs routines correspondent : %s' % ', '.join(l_sub)) 542 print('On utilise la première valeur.') 543 if len(l_sub) == 0: 544 result = None 545 else: 546 result = l_sub[0] 547 return result 548 549 550 def which_msg(self, sub): 551 """Quels sont les messages utilisés par la routine 'sub' ? 552 """ 553 key = self.get_key(sub) 554 return tuple(self.d_msg_used.get(key, [])) 555 556 557template_cata_header = """# -*- coding: utf-8 -*- 558# ====================================================================== 559# COPYRIGHT (C) 1991 - %s EDF R&D WWW.CODE-ASTER.ORG 560# THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY 561# IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY 562# THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR 563# (AT YOUR OPTION) ANY LATER VERSION. 564# 565# THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT 566# WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF 567# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU 568# GENERAL PUBLIC LICENSE FOR MORE DETAILS. 569# 570# YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE 571# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER, 572# 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE. 573# ====================================================================== 574 575def _(x) : return x 576 577cata_msg = {""" % time.strftime('%Y') 578 579template_cata_footer = """ 580} 581""" 582 583template_cata_msg = ''' 584%(msgid)s : _(u"""%(text)s"""),''' 585 586 587class CATA: 588 """Classe représentant un catalogue de messages. 589 Méthodes attendues : 590 - nombre de messages, 591 - indice du dernier messages, 592 - indice libre, 593 - ajouter/supprimer un message, 594 - ... 595 """ 596 def __init__(self, fcata): 597 """Initialisation 598 """ 599 self.fcata = fcata 600 self.cata_msg = {} 601 try: 602 d = { '_' : lambda x: x, } 603 with open(fcata) as f: 604 exec(compile(f.read(), fcata, 'exec'), d) 605 self.cata_msg = d['cata_msg'] 606 except Exception as msg: 607 print('-'*80+'\n', msg, '\n'+'-'*80) 608 raise CataMessageError(self.fcata, _('unable to import the file')) 609 610 611 def get_nb_msg(self): 612 """Nombre de messages. 613 """ 614 return len(self.cata_msg) 615 616 617 def get_last_id(self): 618 """Indice du dernier message. 619 """ 620 return max(list(self.cata_msg.keys()) or [0,]) 621 622 623 def get_new_id(self, reuse_hole=True): 624 """Indice libre. Si 'end', on prend systématiquement à la fin, 625 sinon on cherche aussi dans les trous. 626 """ 627 if not reuse_hole: 628 start = self.get_last_id() 629 else: 630 start = 1 631 all = set(range(start, 100)) 632 free = all.difference(list(self.cata_msg.keys())) 633 if len(free) == 0: 634 new = -1 635 else: 636 new = min(free) 637 return new 638 639 640 def __getitem__(self, key): 641 """Retourne le contenu du message ou None. 642 """ 643 return self.cata_msg.get(key, None) 644 645 646 def __setitem__(self, key, msg): 647 """Ajoute le message 'msg' à l'indice 'key'. 648 """ 649 if self[key] != None: 650 raise MessageError(key, _('message already exists !')) 651 self.cata_msg[key] = msg 652 653 654 def __delitem__(self, key): 655 """Supprime le message d'indice 'key'. 656 """ 657 if self[key] == None: 658 raise MessageError(key, _('this message does not exist !')) 659 del self.cata_msg[key] 660 661 662 def __repr__(self): 663 """Résumé du catalogue. 664 """ 665 return ufmt(_('%3d messages (last=%3d, next=%3d) in %s'), 666 self.get_nb_msg(), self.get_last_id(), self.get_new_id(), self.fcata) 667 668 669 def __iter__(self): 670 """Itération sur les id des messages. 671 """ 672 return iter(self.cata_msg) 673 674 675 def check(self): 676 """Vérifie le texte des messages.""" 677 def_args = {} 678 for i in range(1,100): 679 def_args['i%d' % i] = 99999999 680 def_args['r%d' % i] = 1.234e16 # not too big to avoid python issue1742669 681 def_args['k%d' % i] = 'xxxxxx' 682 def_args['ktout'] = "all strings !" 683 error = [] 684 for num, msg in list(self.cata_msg.items()): 685 if type(msg) is dict: 686 msg = msg['message'] 687 try: 688 txt = msg % def_args 689 except: 690 error.append(ufmt(_('message #%s invalid : %s'), num, msg)) 691 if len(error) > 0: 692 raise CataMessageError(self.fcata, os.linesep.join(error)) 693 694 695 def get_cmodif(self): 696 """Retourne la carte MODIF. 697 """ 698 cmodif = None 699 fobj = open(self.fcata, 'r') 700 for line in fobj: 701 if re.search('#@ +MODIF|#@ +AJOUT', line): 702 cmodif = line.replace(os.linesep, '') 703 break 704 fobj.close() 705 if cmodif == None: 706 raise CataMessageError(self.fcata, _('invalid header "#@ MODIF/AJOUT..."')) 707 return cmodif 708 709 710 def get_content(self): 711 """Contenu du catalogue "reconstruit". 712 """ 713 txt = [self.get_cmodif(),] 714 txt.append(template_cata_header) 715 l_ind = list(self.cata_msg.keys()) 716 l_ind.sort() 717 for msgid in l_ind: 718 txt.append(template_cata_msg % {'msgid' : msgid, 'text' : self.cata_msg[msgid]}) 719 txt.append(template_cata_footer) 720 return os.linesep.join(txt) 721 722 723def clean_source(content, ext, ignore_comments, wrap): 724 """Nettoie un fichier source. 725 """ 726 if ignore_comments: 727 assert ext in ('.f', '.F', '.F90', '.py', '.capy'), 'unknown type : %s' % str(ext) 728 if ext in ('.f', '.F'): 729 reg_ign = re.compile('(^[A-Z!#].*$)', re.MULTILINE) 730 elif ext in ('.F90'): 731 reg_ign = re.compile('(^[!#].*$)', re.MULTILINE) 732 elif ext in ('.py', '.capy'): 733 reg_ign = re.compile('(#.*$)', re.MULTILINE) 734 content = reg_ign.sub('', content).expandtabs() 735 if wrap and ext in ('.f', '.F'): 736 content = ''.join([lin[6:] for lin in content.splitlines()]) 737 return content 738 739 740def GetMessageInfo(run, *args): 741 """Return info about Code_Aster messages. 742 """ 743 if not run.get('aster_vers'): 744 run.parser.error(_("You must define 'default_vers' in 'aster' configuration file or use '--vers' option.")) 745 REPREF = run.get_version_path(run['aster_vers']) 746 fconf = run.get('config') 747 if fconf: 748 fconf = osp.abspath(fconf) 749 conf = build_config_of_version(run, run['aster_vers'], fconf) 750 751 if run['nolocal']: 752 run.Mess(_('This operation only works on local source files. "--nolocal" option ignored')) 753 754 bibfor = [conf['SRCFOR'][0],] 755 if conf['SRCF90'][0] != '': 756 bibfor.append(conf['SRCF90'][0]) 757 758 args = list(args) 759 named_actions = ('check', 'move',) 760 action = None 761 762 if len(args) > 0 and args[0] in named_actions: 763 action = args.pop(0) 764 elif len(args) == 0: 765 run.Mess(_('You must choose an operation from %s or give a subroutine name or a message ID') \ 766 % repr(named_actions), '<F>_INVALID_ARGUMENT') 767 768 # surcharge 769 surch_fort = run.get('surch_fort', []) 770 if surch_fort: 771 surch_fort = surch_fort.split(',') 772 surch_pyt = run.get('surch_pyt', []) 773 if surch_pyt: 774 surch_pyt = surch_pyt.split(',') 775 776 pick_cache = 'messages_dicts.%s.pick' % (run['aster_vers'].replace(os.sep, '_')) 777 msgman = MESSAGE_MANAGER(repref=REPREF, 778 fort=bibfor, 779 pyt=conf['SRCPY'][0], 780 capy=conf['SRCCAPY'][0], 781 cache_dict=osp.join(run['cache_dir'], pick_cache), 782 force=run['force'], verbose=run['verbose'], 783 debug=run['debug'], 784 surch_fort=surch_fort, 785 surch_pyt =surch_pyt, 786 unig =run.get('unigest', None), 787 ) 788 msgman.read_cata() 789 790 if action == 'check': 791 try: 792 unused, not_found = msgman.check_cata() 793 except CataMessageError as msg: 794 run.Sortie(4) 795 if not run['silent']: 796 print('Messages inutilises :') 797 print(' '.join(unused)) 798 print('Messages inexistants :') 799 print(' '.join(not_found)) 800 if len(unused) + len(not_found) > 0: 801 run.Sortie(4) 802 803 elif action == 'move': 804 if len(args) != 2: 805 run.parser.error( 806 _("'--%s %s' requires 2 arguments") % (run.current_action, action)) 807 msgman.move(*args) 808 809 else: 810 print() 811 fmt = '%12s : %s' 812 for obj in args: 813 l_msg = list(set(msgman.which_msg(obj))) 814 if len(l_msg) > 0: 815 l_msg.sort() 816 print(fmt % (obj, l_msg)) 817 l_sub = list(set(msgman.who_use(obj))) 818 if len(l_sub) > 0: 819 l_sub.sort() 820 print(fmt % (obj, l_sub)) 821