Logo Search packages:      
Sourcecode: eficas version File versions  Download package

Utmess.py

#@ MODIF Utmess Utilitai  DATE 16/02/2010   AUTEUR COURTOIS M.COURTOIS 
# -*- coding: iso-8859-1 -*-
#            CONFIGURATION MANAGEMENT OF EDF VERSION
# ======================================================================
# COPYRIGHT (C) 1991 - 2004  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.                                                  
#                                                                       
# 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.                              
#                                                                       
# 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.        
# ======================================================================
# RESPONSABLE COURTOIS M.COURTOIS

import os
import sys
import traceback
import imp
import re

# protection pour eficas
try:
    import aster
    aster_exists = True
    from Messages.context_info import message_context_concept
    AsterError = aster.error
except:
    aster_exists = False
    class AsterError(Exception): pass

def _(s):
    return s

MAXLENGTH = 132

contacter_assistance = """
Il y a probablement une erreur dans la programmation.
Veuillez contacter votre assistance technique."""

# voir en fin de fin les faux appels à UTMESS pour la vérification des messages

00049 class MessageError(Exception):
    """Arguments = (id_message, vali, valr, valk)."""
    def __init__(self, id_message, valk=(), vali=(), valr=()):
        self.id_message = id_message
        self.valk = valk
        self.vali = vali
        self.valr = valr


00058 class MESSAGE_LOGGER:
    """Classe gérant l'impression de messages.
    On ne crée qu'une instance de ce type.
    Cette instance est accessible via le module E_Global pour astermodule.c
    """
00063     def __init__(self):
        """Initialisation
        """
        self.init_buffer()
        
        # est-ce qu'une erreur <E> s'est produite
        self.erreur_E = False
        
        # compteur des alarmes émises { 'id_alarm' : count }
        self.count_alarm = {}         # dans la commande courante (pour arret à 5)
        self.count_alarm_tot = {}     # au total
        
        # alarmes à ignorer, à masquer (on ne les compte pas temporairement)
        self._ignored_alarm = {}
        self._hidden_alarm  = {}
        
        # on prépare le dictionnaire des valeurs par défaut des arguments (dicarg) :
        self.default_args = {}
        # initialisation des 10 premiers
        for i in range(1,11):
            self.default_args['i%d' % i] = 99999999
            self.default_args['r%d' % i] = 9.9999E99
            self.default_args['k%d' % i] = 'xxxxxx'


00088     def __call__(self, *args, **kwargs):
        """Raccourci pour simplifier l'appel depuis astermodule.c et UTMESS.
        """
        self.print_message(*args, **kwargs)


00094     def print_message(self, code, idmess, valk=(), vali=(), valr=(),
                            exception=False, print_as=None, cc=None):
        """Appelé par la routine fortran U2MESG ou à la fonction python UTMESS
        pour afficher un message.
        L'impression de ce message est différée si le `code` est suivi d'un "+".
            code  : 'A', 'E', 'S', 'F', 'I'
            idmess : identificateur du message
            valk, vali, valr : liste des chaines, entiers ou réels.
        Si exception==True, on lève une exception en cas d'erreur.
        'print_as' : cf. print_buffer_content.
        """
        # récupération du texte du message
        dictmess = self.get_message(code, idmess, valk, vali, valr)
        
        # on le met dans le buffer
        self.add_to_buffer(dictmess)
        
        # si on n'attend pas une suite, ...
        if len(code) < 2 or code[1] != '+':
            # mise à jour des compteurs
            self.update_counter()
            
            # on imprime le message en attente
            self.print_buffer_content(print_as, cc)

            if exception and code[0] in ('S', 'F'):
                raise AsterError, ' <EXCEPTION LEVEE> %s' % idmess
        
        return None


00125     def build_dict_args(self, valk, vali, valr):
        """Construit le dictionnaire de formatage du message.
        """
        # homogénéisation : uniquement des tuples + strip des chaines de caractères
        valk, vali, valr = map(force_enum, (valk, vali, valr))
        valk    = [k.strip() for k in valk]
        
        # variables passées au message
        dicarg = self.default_args.copy()
        for i in range(1,len(valk)+1):
            dicarg['k%d' % i] = valk[i-1]
        for i in range(1,len(vali)+1):
            dicarg['i%d' % i] = vali[i-1]
        for i in range(1,len(valr)+1):
            dicarg['r%d' % i] = valr[i-1]
        # valeur spéciale : ktout = concaténation de toutes les chaines
        dicarg['ktout'] = ' '.join(valk)
    
        return dicarg


00146     def get_message(self, code, idmess, valk=(), vali=(), valr=()):
        """Retourne le texte du message dans un dictionnaire dont les clés sont :
            'code', 'id_message', 'corps_message'
        """
        # décodage : idmess => (catamess, numess)
        idmess  = idmess.strip()
        x = idmess.split("_")
        assert len(x) > 1, idmess
        catamess='_'.join(x[0:-1]).lower()
        numess = int(x[-1])
        assert numess > 0 and numess < 100, idmess
    
        # import catamess => cata_msg
        try:
            mod = __import__('Messages.%s' % catamess, globals(), locals(), [catamess])
            # si le dictionnaire n'existe pas, on alertera au moment du formatage.
            cata_msg = getattr(mod, 'cata_msg', {})
        except Exception, msg:
            # doit permettre d'éviter la récursivité (catamess réservé à Utmess)
            if catamess != 'catamess':
                self.print_message('A', 'CATAMESS_57', valk=(catamess, str(msg)))
            cata_msg = {}
        
        # corps du message
        try:
            dicarg = self.build_dict_args(valk, vali, valr)

            # cata_msg[num] = 'format'
            #              ou {'message' : 'format', 'context' : 'éléments de contexte'}
            if type(cata_msg[numess]) == dict:
                fmt_msg  = cata_msg[numess]['message']
                ctxt_msg = cata_msg[numess]['context']
            else:
                fmt_msg  = cata_msg[numess]
                ctxt_msg = None
            
            dictmess = {
                'code'          : code,
                'id_message'    : idmess,
                'corps_message' : fmt_msg % dicarg,
                'context_info'  : self.get_context(ctxt_msg, idmess, dicarg),
            }
        except Exception, msg:
            dictmess = {
                'code'          : code,
                'id_message'    : '',
                'corps_message' : """Erreur de programmation.
Le message %s n'a pas pu etre formaté correctement.
Arguments :
    entiers : %s
    réels   : %s
    chaines : %s
--------------------------------------------------------------------------
%s
Exception : %s
--------------------------------------------------------------------------

%s""" \
        % (idmess, vali, valr, valk, ''.join(traceback.format_tb(sys.exc_traceback)), msg, contacter_assistance),
                'context_info'  : '',
            }
        # limite la longueur des ligness
        dictmess['corps_message'] = cut_long_lines(dictmess['corps_message'], MAXLENGTH)
        return dictmess


00212     def GetText(self, *args, **kwargs):
        """Retourne le texte du message pret a etre imprime.
        """
        return self.format_message(self.get_message(*args, **kwargs))


00218     def init_buffer(self):
        """Initialise le buffer.
        """
        self._buffer = []


00224     def add_to_buffer(self, dictmess):
        """Ajoute le message décrit dans le buffer en vue d'une impression
        ultérieure.
        """
        self._buffer.append(dictmess)


00231     def get_current_code(self):
        """Retourne le code du message du buffer = code du message le plus grave
        (cf. dgrav)
        """
        dgrav = { '?' : -9, 'I' : 0, 'A' : 1, 'S' : 4, 'Z' : 4, 'E' : 6, 'F' : 10 }
        
        current = '?'
        for dictmess in self._buffer:
            code = dictmess['code'][0]
            if dgrav.get(code, -9) > dgrav.get(current, -9):
                current = code
        
        return current


00246     def get_current_id(self):
        """Retourne l'id du message du buffer = id du premier message
        """
        return self._buffer[0]['id_message']


00252     def print_buffer_content(self, print_as=None, cc=None):
        """Extrait l'ensemble des messages du buffer dans un dictionnaire unique,
        imprime le message, et vide le buffer pour le message suivant.
            - code : celui du message le plus grave (cf. dgrav)
            - id   : celui du premier message qui est affiché
            - corps : concaténation de tous les messages.
        'print'_as permet d'imprimer un message sur des fichiers autres que les fichiers
        habituels de 'code'. Par ex, imprimer un message d'info sur 'ERREUR'.
        'cc' : liste de noms de fichiers ou objets fichier dans lesquels copier le message
        """
        if len(self._buffer) < 1:
            return None
        
        # construction du dictionnaire du message global
        dglob = {
            'code'          : self.get_current_code(),
            'id_message'    : self.get_current_id(),
            'liste_message' : [],
            'liste_context' : [],
        }
        for dictmess in self._buffer:
            dglob['liste_message'].append(dictmess['corps_message'])
            dglob['liste_context'].append(dictmess['context_info'])
        dglob['corps_message'] = ''.join(dglob['liste_message'])
        dglob['context_info'] = ''.join(dglob['liste_context'])
        
        # liste des unités d'impression en fonction du type de message
        l_unit = self.list_unit(print_as or dglob['code'], dglob['id_message'])
        
        # texte final et impression
        txt = self.format_message(dglob)
        for unite in l_unit:
            self.affiche(unite, txt)
        # "cc"
        if cc:
            copy_text_to(txt, cc)
        
        self.init_buffer()


00292     def disable_alarm(self, idmess, hide=False):
        """Ignore l'alarme "idmess".
        """
        if hide:
            self._hidden_alarm[idmess] = self._hidden_alarm.get(idmess, 0) + 1
        else:
            self._ignored_alarm[idmess] = self._ignored_alarm.get(idmess, 0) + 1

00300     def reset_alarm(self, idmess, hide=False):
        """Réactive l'alarme "idmess".
        """
        if hide:
            self._hidden_alarm[idmess] = min(self._hidden_alarm.get(idmess, 0) - 1, 0)
        else:
            self._ignored_alarm[idmess] = min(self._ignored_alarm.get(idmess, 0) - 1, 0)

00308     def is_alarm_disabled(self, idmess):
        """Doit-on ignorer l'alarme "idmess" ?
        """
        return self._ignored_alarm.get(idmess, 0) + self._hidden_alarm.get(idmess, 0) > 0

00313     def info_alarm(self, only_ignored=False):
        """Fournit les infos sur les alarmes activées.
        """
        s_alarm = set(self._ignored_alarm.keys())
        if not only_ignored:
            s_alarm.update(self.count_alarm_tot.keys())
        l_alarm = list(s_alarm)
        l_alarm.sort()
        # on sépare des éventuels messages en attente
        self.print_buffer_content()
        # entete
        dictmess = self.get_message('I', 'CATAMESS_89')
        self.add_to_buffer(dictmess)
        # occurrences
        ieff = 0
        for idmess in l_alarm:
            mark = ' '
            ieff += 1
            if self._ignored_alarm.get(idmess) is not None:
                mark = '(*)'
            dictmess = self.get_message('I', 'CATAMESS_90', valk=(mark, idmess),
                                     vali=self.count_alarm_tot.get(idmess, 0))
            self.add_to_buffer(dictmess)
        if ieff == 0:
            dictmess = self.get_message('I', 'CATAMESS_92')
            self.add_to_buffer(dictmess)
        # fermeture
        dictmess = self.get_message('I', 'CATAMESS_91')
        self.add_to_buffer(dictmess)
        self.print_buffer_content(print_as='A')


00345     def update_counter(self):
        """Mise à jour des compteurs et réaction si besoin.
        """
        nmax_alarm = 5
        code = self.get_current_code()
        if   code == 'E':
            self.erreur_E = True
        elif code == 'F':
            self.erreur_E = False
        elif code == 'A':
            idmess = self.get_current_id()
            # nombre d'occurrence de cette alarme (sauf si cachee)
            if self._hidden_alarm.get(idmess, 0) == 0:
                self.count_alarm[idmess]     = self.count_alarm.get(idmess, 0) + 1
                self.count_alarm_tot[idmess] = self.count_alarm_tot.get(idmess, 0) + 1
            
            if self.is_alarm_disabled(idmess) or self.count_alarm[idmess] > nmax_alarm:
                # ignorer l'alarme ou count_alarm > max, on vide le buffer
                self.init_buffer()
            elif self.count_alarm[idmess] == nmax_alarm:
                # Pour mettre en relief le message CATAMESS_41, on le sépare
                # de la dernière alarme
                self.print_buffer_content()
                dictmess = self.get_message(code, 'CATAMESS_41',
                                        valk=idmess, vali=nmax_alarm)
                self.add_to_buffer(dictmess)


00373     def check_counter(self, info_alarm=0, silent=0):
        """Méthode "jusqu'ici tout va bien" ! (Interface C : chkmsg)
        Si des erreurs <E> se sont produites, on arrete le code en <F>.
        Appelée par FIN ou directement au cours de l'exécution d'une commande.
        Retourne un entier : 0 si tout est ok.
        Si silent==1, on n'émet pas de message, on ne s'arrete pas.
        """
        iret = 0
        if self.erreur_E:
            iret = 4
            self.erreur_E = False
            if not silent:
                self.print_message('F', 'CATAMESS_6', exception=True)
        if info_alarm:
            self.info_alarm()
        return iret


00391     def reset_command(self):
        """Méthode appelée entre les commandes. (Interface C : resmsg)
        On remet à zéro le compteur d'alarme,
        on vérifie les erreurs <E> en attente."""
        iret = self.check_counter()
        # reset des alarmes
        self.count_alarm = {}


00400     def format_message(self, dictmess):
        """Formate le message décrit dans un dico :
            'code'          : A, E, S, F, I
            'id_message'    : identification du message
            'corps_message' : texte
        """
        charh = '-'    # horizontal
        charv = '!'    # vertical
        charc = '!'    # coin
        dcomm = {
            'A' : _("""Ceci est une alarme. Si vous ne comprenez pas le sens de cette
alarme, vous pouvez obtenir des résultats inattendus !"""),
            'E' : _("""Cette erreur sera suivie d'une erreur fatale."""),
            'S' : _("""Cette erreur est fatale. Le code s'arrete. Toutes les étapes
du calcul ont été sauvées dans la base jusqu'au moment de l'arret."""),
            'F' : _("""Cette erreur est fatale. Le code s'arrete."""),
        }
        
        # format complet
        format_general = {
            'decal'  : '   ',
            'header' : """<%(type_message)s> %(str_id_message)s""",
            'ligne'  : '%(charv)s %%-%(maxlen)ds %(charv)s',
            'corps'  : """%(header)s

%(corps_message)s
%(context_info)s

%(commentaire)s
""",
            'final'  : """
%(separateur)s
%(corps)s
%(separateur)s

""",
        }
        # format light pour les infos
        format_light = {
            'decal'  : '',
            'header' : """<%(type_message)s> """,
            'ligne'  : '%%s',
            'corps'  : """%(corps_message)s
%(context_info)s""",
            'final'  : """%(corps)s""",
        }
        dmsg = dictmess.copy()
        dmsg['type_message'] = self.get_type_message(dictmess['code'])
        if dmsg['id_message'] != 'I':
            dmsg['str_id_message'] = '<%s>' % dmsg['id_message']
        else:
            dmsg['str_id_message'] = ''
        
        # format utilisé
        format = format_general
        if dmsg['type_message'] == 'I':
            format = format_light
        if format is format_general:
            dmsg['corps_message'] = dmsg['corps_message'].strip()
        
        dmsg['header']      = format['header'] % dmsg
        dmsg['commentaire'] = dcomm.get(dmsg['type_message'], '')
        if re.search('^DVP', dmsg['id_message']) != None:
            dmsg['commentaire'] += contacter_assistance
        
        dmsg['corps']       = format['corps'] % dmsg
        if format is format_general:
            dmsg['corps'] = dmsg['corps'].strip()
        
        
        # longueur de la ligne la plus longue
        l_line = dmsg['corps'].splitlines()
        maxlen = max([len(line) for line in l_line])
        
        # format des lignes sur maxlen caractères
        dlin = {
            'charh'  : charh,
            'charv'  : charv,
            'charc'  : charc,
            'maxlen' : maxlen
        }
        fmt_line = format['ligne'] % dlin
        
        # on formate toutes les lignes
        txt = [fmt_line % line for line in l_line]
        dmsg['corps'] = os.linesep.join(txt)
        dmsg['separateur'] = charc + charh * (maxlen + 2) + charc
        
        # ligne haut et bas
        newtxt = format['final'] % dmsg
        # on décale
        l_txt = [format['decal'] + line for line in newtxt.splitlines()]
        
        return clean_string(os.linesep.join(l_txt))


00496     def list_unit(self, code, idmess):
        """Retourne la liste des noms de fichiers (logiques) sur lesquels doit
        etre imprimé le message.
        """
        #IDF  = INDEX('EFIDASXZ', ...)
        #'Z' (IDF=8) = LEVEE D'EXCEPTION
        d = {
            'E' : ('ERREUR', 'MESSAGE', 'RESULTAT'),
            'I' : ('MESSAGE',),
            'A' : ('MESSAGE', 'RESULTAT'),
        }
        d['F'] = d['S'] = d['Z'] = d['E']
        d['X'] = d['A']
        return d.get(code, d['F'])


00512     def get_type_message(self, code):
        """Retourne le type du message affiché.
        En cas d'erreur, si on lève une exception au lieu de s'arreter,
        on n'affiche pas le type de l'erreur pour ne pas fausser le diagnostic
        """
        typmess = code.strip()
        if self.onFatalError().startswith('EXCEPTION'):
            if typmess in ('E', 'F'):
                typmess = 'EXCEPTION'
        # dans tous les cas, pour S et Z (exception), on affiche EXCEPTION.
        if typmess in ('Z', 'S'):
            typmess = 'EXCEPTION'
        return typmess


00527     def get_context(self, ctxt_msg, idmess, dicarg):
        """Prise en compte du context du message pour donner d'autres infos
        à l'utilisateur.
        ctxt_msg est un dictionnaire. Les clés traitées sont :
            - CONCEPT
        """
        if not ctxt_msg:
            return ''
        msg = []
        # tout dans un try/except car c'est du bonus, il ne faudrait pas planter !
        try:
            if ctxt_msg.has_key('CONCEPT'):
                l_co =  [dicarg[arg] for arg in force_enum(ctxt_msg['CONCEPT'])]
                for co in l_co:
                    msg.append(message_context_concept(co))
        except:
            pass
        return os.linesep.join(msg)


    # définitions pour fonctionner sans le module aster
00548     def affiche(self, unite, txt):
        """Affichage du message"""
        if aster_exists:
            aster.affiche(unite, txt)
        else:
            print txt


00556     def onFatalError(self):
        """Récupérer le comportement en cas d'erreur fatale."""
        if aster_exists:
            return aster.onFatalError()
        else:
            return 'EXCEPTION'


def clean_string(chaine):
    """Supprime tous les caractères non imprimables.
    """
    invalid = '?'
    txt = []
    for c in chaine:
        if ord(c) != 0:
            txt.append(c)
        else:
            txt.append(invalid)
    return ''.join(txt)


def force_enum(obj):
    """Retourne `obj` si c'est une liste ou un tuple,
    sinon retourne [obj,]
    """
    if type(obj) not in (list, tuple):
        obj = [obj,]
    return obj


def maximize_lines(l_fields, maxlen, sep):
    """Construit des lignes dont la longueur est au plus de `maxlen` caractères.
    Les champs sont assemblés avec le séparateur `sep`.
    """
    newlines = []
    while len(l_fields) > 0:
        cur = []
        while len(l_fields) > 0 and len(sep.join(cur + [l_fields[0],])) <= maxlen:
            cur.append(l_fields.pop(0))
        newlines.append(sep.join(cur))
    newlines = [l for l in newlines if l != '']
    return newlines

def cut_long_lines(txt, maxlen, sep=os.linesep,
                   l_separ=(' ', ',', ';', '.', ':')):
    """Coupe les morceaux de `txt` (isolés avec `sep`) de plus de `maxlen`
    caractères.
    On utilise successivement les séparateurs de `l_separ`.
    """
    l_lines = txt.split(sep)
    newlines = []
    for line in l_lines:
        if len(line) > maxlen:
            l_sep = list(l_separ)
            line = cut_long_lines(line, maxlen, l_sep[0], l_sep[1:])
            line = maximize_lines(line, maxlen, l_sep[0])
            newlines.extend(line)
        else:
            newlines.append(line)
    # au plus haut niveau, on assemble le texte
    if sep == os.linesep:
        newlines = os.linesep.join(newlines)
    return newlines


def copy_text_to(text, files):
    """Imprime le texte dans les fichiers.
    """
    if type(files) not in (list, tuple):
        files = [files,]
    for f in files:
        assert type(f) in (str, file)
        if type(f) == file:
            fobj = file
        else:
            fobj = open(f, 'a')
            # should be closed automatically
        fobj.write(text)
        fobj.write(os.linesep)
        fobj.flush()


# unique instance du MESSAGE_LOGGER
MessageLog = MESSAGE_LOGGER()


def UTMESS(code, idmess, valk=(), vali=(), valr=(), print_as=None, cc=None):
    """Utilitaire analogue à la routine fortran U2MESS/U2MESG avec les arguments
    optionnels.
        code   : 'A', 'E', 'S', 'F', 'I'
        idmess : identificateur du message
        valk, vali, valr : liste des chaines, entiers ou réels.
    
    Appel sans valeurs :                avec valeurs :
        UTMESS('A', 'SUPERVIS_40')          UTMESS('A', 'SUPERVIS_40', vali=[1, 2])
    
    Remarques :
        - Nommer les arguments permet de ne pas tous les passer.
        - Meme fonctionnement que U2MESG :
            + appel à MessageLog
            + puis exception ou abort en fonction du niveau d'erreur.
    """
    MessageLog(code, idmess, valk, vali, valr, exception=True, print_as=print_as, cc=cc)


def ASSERT(condition):
    """Remonter un assert dans un message.
    """
    if condition:
        return
    stack = traceback.format_stack(limit=10)
    UTMESS('F', 'DVP_9', valk=(''.join(stack[:-1]),))


def MasquerAlarme(idmess):
    """Masque une alarme : ni affichee, ni comptee.
    Utilisation dans les macros :
        MasquerAlarme(XXX)  au debut de la macro
        RetablirAlarme(XXX) a la fin de la macro
    Comme il s'agit d'un compteur qui est incremente puis decremente, il est
    imperatif qu'il y ait autant d'appel a MasquerAlarme qu'a RetablirAlarme.
    """
    MessageLog.disable_alarm(idmess, hide=True)


def RetablirAlarme(idmess):
    """Retablit l'etat initial pour l'alarme 'idmess'.
    """
    MessageLog.reset_alarm(idmess, hide=True)


# faux appels à UTMESS
def __fake__():
    UTMESS('I', 'SUPERVIS_40')    # surcharge émis par asrun
    UTMESS('I', 'CATAMESS_6')
    UTMESS('I', 'CATAMESS_41')
    UTMESS('I', 'CATAMESS_55')    # pour u2mesg.f
    UTMESS('I', 'CATAMESS_57')
    UTMESS('I', 'CATAMESS_69')    # pour u2mesg.f
    UTMESS('I', 'CATAMESS_70')    # pour u2mesg.f
    UTMESS('I', 'CATAMESS_89')
    UTMESS('I', 'CATAMESS_90')
    UTMESS('I', 'CATAMESS_91')
    UTMESS('I', 'CATAMESS_92')
    UTMESS('I', 'MISS0_3')
    UTMESS('I', 'MISS0_5')
    UTMESS('I', 'MISS0_6')
    UTMESS('I', 'MISS0_7')
    UTMESS('I', 'MISS0_8')



Generated by  Doxygen 1.6.0   Back to index