From d96f13b71e9d6e4f0d043368239207f973b120f5 Mon Sep 17 00:00:00 2001 From: Christian Caremoli <> Date: Fri, 19 May 2006 15:22:52 +0000 Subject: [PATCH] CCAR: mise a niveau avec Aster stabilise : mecanique de validation et regles de sensibilite --- Accas/A_FACT.py | 6 + Accas/A_SENSIBILITE.py | 9 + Accas/A_VALIDATOR.py | 46 +- Accas/__init__.py | 37 +- Editeur/Objecttreeitem.py | 5 +- Editeur/compomclist.py | 6 + Extensions/param2.py | 8 +- Extensions/parametre.py | 2 +- Ihm/I_ASSD.py | 6 +- Ihm/I_MCSIMP.py | 4 +- Ihm/I_OBJECT.py | 3 + Ihm/I_VALIDATOR.py | 261 +-------- Noyau/N_ASSD.py | 27 +- Noyau/N_CO.py | 23 +- Noyau/N_ETAPE.py | 13 +- Noyau/N_FACT.py | 18 +- Noyau/N_GEOM.py | 14 +- Noyau/N_JDC.py | 12 +- Noyau/N_MACRO_ETAPE.py | 28 +- Noyau/N_OBJECT.py | 32 +- Noyau/N_SENSIBILITE.py | 180 ++++++ Noyau/N_VALIDATOR.py | 978 ++++++++++++++++++++----------- Tests/HTMLTestRunner.py | 604 +++++++++++++------ Tests/config.py | 4 +- Tests/testelem/cata1.py | 5 + Tests/testelem/cata5.py | 19 + Tests/testelem/testfact1.py | 17 +- Tests/testelem/testjdc1.py | 12 +- Tests/testelem/testjdc2.py | 8 +- Tests/testelem/testoper1.py | 25 +- Tests/testelem/testposition1.py | 28 +- Tests/testelem/testsimp1.py | 28 +- Tests/testelem/testsimp2.py | 3 +- Tests/testelem/testsimp3.py | 185 ++++-- Tests/testelem/testsimp4.py | 63 +- Tests/testelem/testvalidator1.py | 37 ++ Tests/testelem/testvalidator2.py | 240 ++++++-- Validation/V_MCCOMPO.py | 4 +- Validation/V_MCSIMP.py | 321 ++-------- 39 files changed, 1980 insertions(+), 1341 deletions(-) create mode 100644 Accas/A_SENSIBILITE.py create mode 100644 Noyau/N_SENSIBILITE.py diff --git a/Accas/A_FACT.py b/Accas/A_FACT.py index 4831aaa1..9db138b7 100644 --- a/Accas/A_FACT.py +++ b/Accas/A_FACT.py @@ -30,3 +30,9 @@ class FACT(N_FACT.FACT,I_ENTITE.ENTITE): I_ENTITE.ENTITE.__init__(self) N_FACT.FACT.__init__(self,*tup,**args) +from Noyau import N_OBJECT +from Ihm import I_OBJECT + +class ErrorObj(I_OBJECT.ErrorObj,N_OBJECT.ErrorObj):pass +N_OBJECT.ErrorObj=ErrorObj + diff --git a/Accas/A_SENSIBILITE.py b/Accas/A_SENSIBILITE.py new file mode 100644 index 00000000..f6581ee2 --- /dev/null +++ b/Accas/A_SENSIBILITE.py @@ -0,0 +1,9 @@ + +from Ihm import I_REGLE + +from Noyau import N_SENSIBILITE + +class REUSE_SENSIBLE(I_REGLE.REGLE,N_SENSIBILITE.REUSE_SENSIBLE):pass +class CONCEPT_SENSIBLE(I_REGLE.REGLE,N_SENSIBILITE.CONCEPT_SENSIBLE):pass +class DERIVABLE(I_REGLE.REGLE,N_SENSIBILITE.DERIVABLE):pass + diff --git a/Accas/A_VALIDATOR.py b/Accas/A_VALIDATOR.py index 2f0d2978..580836d0 100644 --- a/Accas/A_VALIDATOR.py +++ b/Accas/A_VALIDATOR.py @@ -1,46 +1,2 @@ # -*- coding: utf-8 -*- -import types -from Noyau import N_VALIDATOR -from Ihm import I_VALIDATOR -from Ihm.I_VALIDATOR import ValidException - -class FunctionVal(I_VALIDATOR.FunctionVal,N_VALIDATOR.FunctionVal):pass -class OrVal(I_VALIDATOR.OrVal,N_VALIDATOR.OrVal):pass -class AndVal(I_VALIDATOR.AndVal,N_VALIDATOR.AndVal):pass -class NoRepeat(I_VALIDATOR.NoRepeat,N_VALIDATOR.NoRepeat):pass -class LongStr(I_VALIDATOR.LongStr,N_VALIDATOR.LongStr):pass -class OrdList(I_VALIDATOR.OrdList,N_VALIDATOR.OrdList):pass -class RangeVal(I_VALIDATOR.RangeVal,N_VALIDATOR.RangeVal):pass -class EnumVal(I_VALIDATOR.EnumVal,N_VALIDATOR.EnumVal):pass -class TypeVal(I_VALIDATOR.TypeVal,N_VALIDATOR.TypeVal):pass -class PairVal(I_VALIDATOR.PairVal,N_VALIDATOR.PairVal):pass -class CardVal(I_VALIDATOR.CardVal,N_VALIDATOR.CardVal):pass -class InstanceVal(I_VALIDATOR.InstanceVal,N_VALIDATOR.InstanceVal):pass - -def do_liste(validators): - """ - Convertit une arborescence de validateurs en OrVal ou AndVal - validators est une liste de validateurs ou de listes ou de tuples - """ - valids=[] - for validator in validators: - if type(validator) == types.FunctionType: - valids.append(FunctionVal(validator)) - elif type(validator) == types.TupleType: - valids.append(OrVal(do_liste(validator))) - elif type(validator) == types.ListType: - valids.append(AndVal(do_liste(validator))) - else: - valids.append(validator) - return valids - -def validatorFactory(validator): - if type(validator) == types.FunctionType: - return FunctionVal(validator) - elif type(validator) == types.TupleType: - return OrVal(do_liste(validator)) - elif type(validator) == types.ListType: - return AndVal(do_liste(validator)) - else: - return validator - +from Ihm.I_VALIDATOR import * diff --git a/Accas/__init__.py b/Accas/__init__.py index 1eb50497..8ba10bf6 100644 --- a/Accas/__init__.py +++ b/Accas/__init__.py @@ -1,7 +1,7 @@ -# -*- coding: utf-8 -*- +# -*- coding: iso-8859-1 -*- # CONFIGURATION MANAGEMENT OF EDF VERSION # ====================================================================== -# COPYRIGHT (C) 1991 - 2002 EDF R&D WWW.CODE-ASTER.ORG +# COPYRIGHT (C) 1991 - 2001 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 @@ -15,20 +15,26 @@ # 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 package contient les classes qui seront effectivement utilisées dans les applications. - C'est dans ce package que sont réalisées les combinaisons de classes de base - avec les classes MIXIN qui implémentent les fonctionnalités qui ont été séparées - du noyau pour des raisons de modularité afin de faciliter la maintenance et - l'extensibilité. + Ce package contient les classes qui seront effectivement utilisees dans les applications. + C'est dans ce package que sont realisees les combinaisons de classes de base + avec les classes MIXIN qui implementent les fonctionnalites qui ont ete separees + du noyau pour des raisons de modularite afin de faciliter la maintenance et + l'extensibilite. - De plus toutes les classes utilisables par les applications sont remontées au - niveau du package afin de rendre le plus indépendant possible l'utilisation des - classes et leur implémentation. + De plus toutes les classes utilisables par les applications sont remontees au + niveau du package afin de rendre le plus independant possible l'utilisation des + classes et leur implementation. """ + +# permet de se proteger de l'oubli de carte coding +# ce warning deviendra fatal en python 2.4 +import warnings +warnings.filterwarnings('error','Non-ASCII character.*pep-0263',DeprecationWarning) + from A_JDC_CATA import JDC_CATA from A_OPER import OPER from A_PROC import PROC @@ -61,7 +67,6 @@ from A_ENSEMBLE import ENSEMBLE from A_A_CLASSER import A_CLASSER from A_ASSD import ASSD,assd -#from A_ASSD import LASSD from A_ASSD import GEOM,geom # Pour le moment on laisse fonction (ceinture et bretelles) from A_ASSD import FONCTION, fonction @@ -73,11 +78,10 @@ from Noyau.N__F import _F from Noyau.N_Exception import AsException from Noyau.N_utils import AsType -#from Noyau.N_VALIDATOR import Valid,RangeVal,OrdList,NoRepeat,LongStr,EnumVal,CardVal,TypeVal,InstanceVal,OrVal,AndVal -from A_VALIDATOR import OrdList,NoRepeat,LongStr,OrVal,AndVal +from A_VALIDATOR import OrVal,AndVal +from A_VALIDATOR import OrdList,NoRepeat,LongStr,Compulsory from A_VALIDATOR import RangeVal, EnumVal, TypeVal, PairVal from A_VALIDATOR import CardVal, InstanceVal -from A_VALIDATOR import ValidException # On remplace la factory des validateurs initialement dans Noyau par celle # de A_VALIDATOR @@ -85,6 +89,7 @@ import A_VALIDATOR import Noyau.N_ENTITE Noyau.N_ENTITE.ENTITE.factories['validator']=A_VALIDATOR.validatorFactory +from A_SENSIBILITE import CONCEPT_SENSIBLE, REUSE_SENSIBLE, DERIVABLE from Extensions.niveau import NIVEAU from Extensions.etape_niveau import ETAPE_NIVEAU diff --git a/Editeur/Objecttreeitem.py b/Editeur/Objecttreeitem.py index b53635cb..21998b57 100644 --- a/Editeur/Objecttreeitem.py +++ b/Editeur/Objecttreeitem.py @@ -365,7 +365,10 @@ class ObjectTreeItem(TreeItem,Delegate): def get_fr(self): """ Retourne le fr de l'objet pointé par self """ - return self.object.get_fr() + try: + return self.object.get_fr() + except: + return "" def get_docu(self): """ Retourne la clé de doc de l'objet pointé par self """ diff --git a/Editeur/compomclist.py b/Editeur/compomclist.py index 46c66f61..ae92e03a 100644 --- a/Editeur/compomclist.py +++ b/Editeur/compomclist.py @@ -21,6 +21,8 @@ import types from Tkinter import * import Pmw + +from Noyau.N_OBJECT import ErrorObj import Objecttreeitem import panels import traceback @@ -44,7 +46,9 @@ class MCLISTPanel(panels.Panel): self.node.parent.append_child(self.node.item.get_nom()) import compofact +import compoerror import treewidget + class Node(treewidget.Node): def doPaste(self,node_selected): objet_a_copier = self.item.get_copie_objet() @@ -105,6 +109,8 @@ class MCListTreeItem(Objecttreeitem.SequenceTreeItem,compofact.FACTTreeItem): """ if len(self._object) > 1: return MCLISTPanel(jdcdisplay,pane,node) + elif isinstance(self._object.data[0],ErrorObj): + return compoerror.ERRORPanel(jdcdisplay,pane,node) else: return compofact.FACTPanel(jdcdisplay,pane,node) diff --git a/Extensions/param2.py b/Extensions/param2.py index 5d307aac..23dd105c 100644 --- a/Extensions/param2.py +++ b/Extensions/param2.py @@ -69,7 +69,7 @@ class Binop(Formula): result=result.eval() return result def __adapt__(self,validator): - return validator(self.eval()) + return validator.adapt(self.eval()) original_sqrt=math.sqrt original_ceil=math.ceil @@ -97,7 +97,7 @@ class Unop(Formula): def eval(self): return self.opmap[self._op](self._arg.eval()) def __adapt__(self,validator): - return validator(self.eval()) + return validator.adapt(self.eval()) class Unop2(Unop): def __init__(self, nom, op, arg): @@ -129,7 +129,7 @@ class Constant(Formula): def eval(self): return self._value def __str__(self): return str(self._value) def __adapt__(self,validator): - return validator(self._value) + return validator.adapt(self._value) class Variable(Formula): def __init__(self,name,value): @@ -139,7 +139,7 @@ class Variable(Formula): def __repr__(self): return "Variable('%s',%s)" % (self._name, self._value) def __str__(self): return self._name def __adapt__(self,validator): - return validator(self._value) + return validator.adapt(self._value) def cos(f): return Unop('ncos', f) def sin(f): return Unop('nsin', f) diff --git a/Extensions/parametre.py b/Extensions/parametre.py index f94e2036..54025443 100644 --- a/Extensions/parametre.py +++ b/Extensions/parametre.py @@ -335,7 +335,7 @@ class PARAMETRE(N_OBJECT.OBJECT,I_OBJECT.OBJECT,Formula) : return self.valeur def __adapt__(self,validator): - return validator(self.eval()) + return validator.adapt(self.eval()) class COMBI_PARAMETRE : def __init__(self,chainevaleur,valeur): diff --git a/Ihm/I_ASSD.py b/Ihm/I_ASSD.py index 15b7e162..d7ade31b 100644 --- a/Ihm/I_ASSD.py +++ b/Ihm/I_ASSD.py @@ -19,7 +19,8 @@ # # ====================================================================== -from I_VALIDATOR import ValidException +#from I_VALIDATOR import ValidException +from Noyau.N_VALIDATOR import ValError class ASSD: def __repr__(self): @@ -46,6 +47,7 @@ class CO(ASSD): if valeur.etape == valeur._etape: # le concept est bien produit par l'etape return valeur - raise ValidException("Pas un concept CO") + raise ValError("Pas un concept CO") + #raise ValidException("Pas un concept CO") __convert__=classmethod(__convert__) diff --git a/Ihm/I_MCSIMP.py b/Ihm/I_MCSIMP.py index b25a31bf..0723393f 100644 --- a/Ihm/I_MCSIMP.py +++ b/Ihm/I_MCSIMP.py @@ -48,7 +48,7 @@ from Extensions import param2 import I_OBJECT import CONNECTOR -from I_VALIDATOR import ValidException +#from I_VALIDATOR import ValidException class MCSIMP(I_OBJECT.OBJECT): @@ -798,7 +798,7 @@ class MCSIMP(I_OBJECT.OBJECT): if cr == 'oui': self.cr.fatal(str(e)) return 0 - def isvalid(self,cr='non'): + def isvalid_BAK(self,cr='non'): """ Cette méthode retourne un indicateur de validité de l'objet de type MCSIMP diff --git a/Ihm/I_OBJECT.py b/Ihm/I_OBJECT.py index 99a54efb..d7d7c164 100644 --- a/Ihm/I_OBJECT.py +++ b/Ihm/I_OBJECT.py @@ -155,3 +155,6 @@ class OBJECT: #def __del__(self): # print "__del__",self + +class ErrorObj(OBJECT):pass + diff --git a/Ihm/I_VALIDATOR.py b/Ihm/I_VALIDATOR.py index 23c34f7c..1c94ece7 100644 --- a/Ihm/I_VALIDATOR.py +++ b/Ihm/I_VALIDATOR.py @@ -1,260 +1 @@ -# -*- coding: utf-8 -*- -""" - Ce module contient des classes permettant de définir des validateurs - pour EFICAS. Ces classes constituent un complément à des classes existantes - dans Noyau/N_VALIDATOR.py ou de nouvelles classes de validation. - Ces classes complémentaires ne servent que pour l'IHM d'EFICAS. - Elles servent essentiellement à ajouter des comportements spécifiques - IHM aux classes existantes dans le Noyau. - Ces comportements pourront etre rapatries dans le Noyau quand leur - interface sera stabilisée. -""" -class ValidException(Exception):pass - -import types -from I_MCSIMP import ValidException - -class Valid: - """ - Cette classe est la classe mere de toutes les classes complémentaires - que l'on trouve dans Ihm. - """ - -class ListVal(Valid): - """ - Cette classe sert de classe mère pour tous les validateurs qui acceptent - des listes. - """ - def is_list(self): - return 1 - - def get_into(self,liste_courante=None,into_courant=None): - """ - Cette méthode get_into effectue un traitement général qui consiste - a filtrer la liste de choix into_courant, si elle existe, en ne - conservant que les valeurs valides (appel de la méthode valid). - """ - if into_courant is None: - return None - else: - liste_choix=[] - for e in into_courant: - if self.verif(e): - liste_choix.append(e) - return liste_choix - - def convert(self,valeur): - """ - Méthode verif pour les validateurs de listes. Cette méthode - fait appel à la méthode verif_item sur chaque élément de la - liste. Si valeur est un paramètre, on utilise sa valeur effective - valeur.valeur. - """ - if type(valeur) in (types.ListType,types.TupleType): - for val in valeur: - self.convert_item(val) - return valeur - else: - return self.convert_item(valeur) - - -class RangeVal(ListVal): - def convert_item(self,valeur): - if valeur > self.low and valeur < self.high:return valeur - raise ValidException("%s devrait etre comprise entre %s et %s" %(valeur,self.low,self.high)) - - -class CardVal(Valid): - def convert(self,valeur): - if type(valeur) in (types.ListType,types.TupleType): - l=len(valeur) - elif valeur is None: - l=0 - else: - l=1 - if self.max != '**' and l > self.max:raise ValidException("%s devrait etre de longueur inferieure a %s" %(valeur,self.max)) - if self.min != '**' and l < self.min:raise ValidException("%s devrait etre de longueur superieure a %s" %(valeur,self.min)) - return valeur - - -class PairVal(ListVal): - def convert(self,valeur): - if type(valeur) in (types.ListType,types.TupleType): - for val in valeur: - if val % 2 != 0:raise ValidException("%s contient des valeurs non paires" % repr(valeur)) - else: - if valeur % 2 != 0:raise ValidException("%s n'est pas pair" % repr(valeur)) - return valeur - - - -class EnumVal(ListVal): - def convert_item(self,valeur): - if valeur in self.into:return valeur - raise ValidException("%s contient des valeurs hors des choix possibles: %s " %(valeur,self.into)) - - -class NoRepeat(ListVal): - """ - Verification d'absence de doublons dans la liste. - """ - def __init__(self): - self.cata_info="" - - def info(self): - return ": pas de présence de doublon dans la liste" - - def info_erreur_liste(self): - return "Les doublons ne sont pas permis" - - def verif_item(self,valeur): - return 1 - - def verif(self,valeur): - if type(valeur) in (types.ListType,types.TupleType): - liste=list(valeur) - for val in liste: - if liste.count(val)!=1 : return 0 - return 1 - else: - return 1 - - def convert_item(self,valeur): - if valeur in self.liste : raise ValidException("%s est un doublon" % valeur) - return valeur - - def convert(self,valeur): - adapt = getattr(valeur, '__adapt__', None) - if adapt is not None: - # l'objet valeur peut se verifier lui meme - return adapt(self.convert) - - if type(valeur) == types.TupleType and not valeur[0] in ('RI','MP') or type(valeur) == types.ListType: - # Cas d'une liste de valeurs - # on s'arrete a la premiere erreur - self.liste=[] - for val in valeur: - adapt = getattr(val, '__adapt__', None) - if adapt is not None: - v=adapt(self.convert_item) - else: - v=self.convert_item(val) - self.liste.append(v) - return valeur - - def get_into(self,liste_courante=None,into_courant=None): - """ - Methode get_into spécifique pour validateur NoRepeat, on retourne - une liste de choix qui ne contient aucune valeur de into_courant - déjà contenue dans liste_courante - """ - if into_courant is None: - liste_choix=None - else: - liste_choix=[] - for e in into_courant: - if e in liste_choix: continue - if liste_courante is not None and e in liste_courante: continue - liste_choix.append(e) - return liste_choix - - -class LongStr(ListVal): - """ - Verification de la longueur d une chaine - """ - def __init__(self,low,high): - self.low=low - self.high=high - self.cata_info="" - - def info(self): - return "longueur de la chaine entre %s et %s" %(self.low,self.high) - - def info_erreur_item(self): - return "Longueur de la chaine incorrecte" - - def verif_item(self,valeur): - low=self.low - high=self.high - if valeur[0]=="'" and valeur[-1]=="'" : - low=low+2 - high=high+2 - if len(valeur) < low :return 0 - if len(valeur) > high:return 0 - return 1 - - def convert(self,valeur): - adapt = getattr(valeur, '__adapt__', None) - if adapt is not None: - # l'objet valeur peut se verifier lui meme - return adapt(self.convert) - - if type(valeur) == types.TupleType and not valeur[0] in ('RI','MP') or type(valeur) == types.ListType: - # Cas d'une liste de valeurs - # on s'arrete a la premiere erreur - for val in valeur: - self.convert_item(val) - return valeur - else: - return self.convert_item(valeur) - - def convert_item(self,valeur): - low=self.low - high=self.high - if valeur[0]=="'" and valeur[-1]=="'" : - low=low+2 - high=high+2 - if len(valeur) < low or len(valeur) > high : - raise ValidException("%s n'est pas de la bonne longueur" % repr(valeur)) - return valeur - - - -class OrdList(ListVal): - def convert(self,valeur): - if type(valeur) in (types.ListType,types.TupleType): - if self.ord=='croissant': - var=valeur[0] - for val in valeur[1:]: - if valvar:raise ValidException("%s n'est pas par valeurs decroissantes" % repr(valeur)) - var=val - return valeur - else: - return valeur - - -CoercableFuncs = { types.IntType: int, - types.LongType: long, - types.FloatType: float, - types.ComplexType: complex, - types.UnicodeType: unicode } - -class TypeVal(ListVal): - def convert_item(self,valeur): - return self.coerce(valeur) - -class InstanceVal(ListVal):pass - -class FunctionVal(Valid):pass - -class OrVal(Valid): - def convert(self,valeur): - for validator in self.validators: - try: - return validator.verif(valeur) - except: - pass - raise ValidException("%s n'est pas du bon type" % valeur) - -class AndVal(Valid): - def convert(self,valeur): - for validator in self.validators: - valeur=validator.convert(valeur) - return valeur +from Noyau.N_VALIDATOR import * diff --git a/Noyau/N_ASSD.py b/Noyau/N_ASSD.py index cae723c5..c8920fbc 100644 --- a/Noyau/N_ASSD.py +++ b/Noyau/N_ASSD.py @@ -1,4 +1,4 @@ -#@ MODIF N_ASSD Noyau DATE 22/02/2005 AUTEUR DURAND C.DURAND +#@ MODIF N_ASSD Noyau DATE 16/05/2006 AUTEUR DURAND C.DURAND # -*- coding: iso-8859-1 -*- # CONFIGURATION MANAGEMENT OF EDF VERSION # ====================================================================== @@ -61,13 +61,6 @@ class ASSD: def __getitem__(self,key): return self.etape[key] - def is_object(valeur): - """ - Indique si valeur est d'un type conforme à la classe (retourne 1) - ou non conforme (retourne 0) - """ - return 0 - def get_name(self): """ Retourne le nom de self, éventuellement en le demandant au JDC @@ -111,13 +104,17 @@ class ASSD: if key[0]=='_':del d[key] return d -class assd(ASSD): - def is_object(valeur): + def par_lot(self): """ - Indique si valeur est d'un type conforme à la classe (1) - ou non conforme (0) - La classe assd est utilisée pour valider tout objet + Retourne True si l'ASSD est créée en mode PAR_LOT='OUI'. """ - return 1 - + if not hasattr(self, 'jdc') or self.jdc == None: + val = None + else: + val = self.jdc.par_lot + return val == 'OUI' +class assd(ASSD): + def __convert__(cls,valeur): + return valeur + __convert__=classmethod(__convert__) diff --git a/Noyau/N_CO.py b/Noyau/N_CO.py index 3f7a3ac7..60750a87 100644 --- a/Noyau/N_CO.py +++ b/Noyau/N_CO.py @@ -1,4 +1,4 @@ -#@ MODIF N_CO Noyau DATE 22/02/2005 AUTEUR DURAND C.DURAND +#@ MODIF N_CO Noyau DATE 16/05/2006 AUTEUR DURAND C.DURAND # -*- coding: iso-8859-1 -*- # CONFIGURATION MANAGEMENT OF EDF VERSION # ====================================================================== @@ -23,6 +23,7 @@ from N_ASSD import ASSD from N_Exception import AsException +from N_VALIDATOR import ValError import N_utils class CO(ASSD): @@ -40,15 +41,11 @@ class CO(ASSD): else: self.nom=nom - def is_object(valeur): - """ - Indique si valeur est d'un type conforme à la classe (retourne 1) - ou non conforme (retourne 0) - """ - if hasattr(valeur,'_etape') : - # valeur est un concept CO qui a ete transforme par type_sdprod - if valeur.etape == valeur._etape: - # le concept est bien produit par l'etape - return 1 - return 0 - + def __convert__(cls,valeur): + if hasattr(valeur,'_etape') : + # valeur est un concept CO qui a ete transforme par type_sdprod + if valeur.etape == valeur._etape: + # le concept est bien produit par l'etape + return valeur + raise ValError("Pas un concept CO") + __convert__=classmethod(__convert__) diff --git a/Noyau/N_ETAPE.py b/Noyau/N_ETAPE.py index 377ce752..dde08f5b 100644 --- a/Noyau/N_ETAPE.py +++ b/Noyau/N_ETAPE.py @@ -1,4 +1,4 @@ -#@ MODIF N_ETAPE Noyau DATE 22/02/2005 AUTEUR DURAND C.DURAND +#@ MODIF N_ETAPE Noyau DATE 10/05/2006 AUTEUR MCOURTOI M.COURTOIS # -*- coding: iso-8859-1 -*- # CONFIGURATION MANAGEMENT OF EDF VERSION # ====================================================================== @@ -117,7 +117,6 @@ class ETAPE(N_MCCOMPO.MCCOMPO): le nommage du concept. """ - if not self.isactif():return self.sdnom=nom try: if self.parent: @@ -356,6 +355,8 @@ class ETAPE(N_MCCOMPO.MCCOMPO): self.etape=self for mocle in self.mc_liste: mocle.reparent(self) + if self.sd and self.reuse == None : + self.sd.jdc=self.jdc def get_cmd(self,nomcmd): """ @@ -393,3 +394,11 @@ class ETAPE(N_MCCOMPO.MCCOMPO): new_sd.nom = self.sd.nom new_etape.copy_intern(self) return new_etape + + def reset_jdc(self,new_jdc): + """ + Reinitialise le nommage du concept de l'etape lors d'un changement de jdc + """ + if self.sd and self.reuse == None : + self.parent.NommerSdprod(self.sd,self.sd.nom) + diff --git a/Noyau/N_FACT.py b/Noyau/N_FACT.py index c92c5934..e5e3b428 100644 --- a/Noyau/N_FACT.py +++ b/Noyau/N_FACT.py @@ -1,4 +1,4 @@ -#@ MODIF N_FACT Noyau DATE 14/09/2004 AUTEUR MCOURTOI M.COURTOIS +#@ MODIF N_FACT Noyau DATE 16/05/2006 AUTEUR DURAND C.DURAND # -*- coding: iso-8859-1 -*- # CONFIGURATION MANAGEMENT OF EDF VERSION # ====================================================================== @@ -30,6 +30,9 @@ import types import N_ENTITE import N_MCFACT import N_MCLIST +from N__F import _F + +import N_OBJECT class FACT(N_ENTITE.ENTITE): """ @@ -115,7 +118,7 @@ class FACT(N_ENTITE.ENTITE): elif type(self.defaut) == types.TupleType: val=self.defaut # Est ce utile ? Le défaut pourrait etre uniquement un dict - elif type(self.defaut) == types.DictType or isinstance(self.defaut,N_MCFACT._F): + elif type(self.defaut) == types.DictType or isinstance(self.defaut,_F): val=self.defaut else: # On ne devrait jamais passer par la @@ -127,11 +130,16 @@ class FACT(N_ENTITE.ENTITE): l.init(nom = nom,parent=parent) if type(val) in (types.TupleType,types.ListType) : for v in val: - objet=self.class_instance(nom=nom,definition=self,val=v,parent=parent) - l.append(objet) - else: + if type(v) == types.DictType or isinstance(v, _F): + objet=self.class_instance(nom=nom,definition=self,val=v,parent=parent) + l.append(objet) + else: + l.append(N_OBJECT.ErrorObj(self,v,parent,nom)) + elif type(val) == types.DictType or isinstance(val, _F): objet=self.class_instance(nom=nom,definition=self,val=val,parent=parent) l.append(objet) + else: + l.append(N_OBJECT.ErrorObj(self,val,parent,nom)) return l diff --git a/Noyau/N_GEOM.py b/Noyau/N_GEOM.py index 70158ae9..0fc610d7 100644 --- a/Noyau/N_GEOM.py +++ b/Noyau/N_GEOM.py @@ -1,4 +1,4 @@ -#@ MODIF N_GEOM Noyau DATE 14/09/2004 AUTEUR MCOURTOI M.COURTOIS +#@ MODIF N_GEOM Noyau DATE 16/05/2006 AUTEUR DURAND C.DURAND # -*- coding: iso-8859-1 -*- # CONFIGURATION MANAGEMENT OF EDF VERSION # ====================================================================== @@ -55,15 +55,9 @@ class GEOM(ASSD): def get_name(self): return self.nom - def is_object(valeur): - """ - Indique si valeur est d'un type conforme à la classe (1) - ou non conforme (0) - La classe GEOM est utilisée pour tous les objets géométriques - Elle valide tout objet - """ - return 1 - + def __convert__(cls,valeur): + return valeur + __convert__=classmethod(__convert__) class geom(GEOM):pass diff --git a/Noyau/N_JDC.py b/Noyau/N_JDC.py index b4f4539c..5c39d072 100644 --- a/Noyau/N_JDC.py +++ b/Noyau/N_JDC.py @@ -1,4 +1,4 @@ -#@ MODIF N_JDC Noyau DATE 05/09/2005 AUTEUR DURAND C.DURAND +#@ MODIF N_JDC Noyau DATE 10/05/2006 AUTEUR MCOURTOI M.COURTOIS # -*- coding: iso-8859-1 -*- # CONFIGURATION MANAGEMENT OF EDF VERSION # ====================================================================== @@ -412,7 +412,7 @@ NONE = None # courante pendant le processus de construction des étapes. # Si on insère des commandes (par ex, dans EFICAS), il faut préalablement # remettre ce pointeur à 0 - if etape is not None: + if etape: index_etape=self.etapes.index(etape) else: index_etape=len(self.etapes) @@ -448,3 +448,11 @@ NONE = None if hasattr(cata,nomcmd): return getattr(cata,nomcmd) + def append_reset(self,etape): + """ + Ajoute une etape provenant d'un autre jdc a la liste des etapes + et remet à jour la parenté de l'étape et des concepts + """ + self.etapes.append(etape) + etape.reparent(self) + etape.reset_jdc(self) diff --git a/Noyau/N_MACRO_ETAPE.py b/Noyau/N_MACRO_ETAPE.py index 87c14e47..43ff1d7d 100644 --- a/Noyau/N_MACRO_ETAPE.py +++ b/Noyau/N_MACRO_ETAPE.py @@ -1,4 +1,4 @@ -#@ MODIF N_MACRO_ETAPE Noyau DATE 31/05/2005 AUTEUR DURAND C.DURAND +#@ MODIF N_MACRO_ETAPE Noyau DATE 10/05/2006 AUTEUR MCOURTOI M.COURTOIS # -*- coding: iso-8859-1 -*- # CONFIGURATION MANAGEMENT OF EDF VERSION # ====================================================================== @@ -114,7 +114,6 @@ class MACRO_ETAPE(N_ETAPE.ETAPE): 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 @@ -643,9 +642,22 @@ Le type demande (%s) et le type du concept (%s) devraient etre derives""" %(t,co new_etp.copy_intern(etp) self.etapes.append(new_etp) - - - - - - + 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 parenté des concepts. On s'assure uniquement que le jdc en référence est le bon + for concept in self.sdprods: + concept.jdc=self.jdc + for e in self.etapes: + e.reparent(self) diff --git a/Noyau/N_OBJECT.py b/Noyau/N_OBJECT.py index 5f6061a3..45c96cf2 100644 --- a/Noyau/N_OBJECT.py +++ b/Noyau/N_OBJECT.py @@ -1,4 +1,4 @@ -#@ MODIF N_OBJECT Noyau DATE 14/09/2004 AUTEUR MCOURTOI M.COURTOIS +#@ MODIF N_OBJECT Noyau DATE 16/05/2006 AUTEUR DURAND C.DURAND # -*- coding: iso-8859-1 -*- # CONFIGURATION MANAGEMENT OF EDF VERSION # ====================================================================== @@ -25,6 +25,7 @@ Ce module contient la classe OBJECT classe mère de tous les objets servant à controler les valeurs par rapport aux définitions """ +from N_CR import CR class OBJECT: """ @@ -99,3 +100,32 @@ class OBJECT: self.parent=parent self.jdc=parent.jdc +class ErrorObj(OBJECT): + """Classe pour objets errones : emule le comportement d'un objet tel mcsimp ou mcfact + """ + def __init__(self,definition,valeur,parent,nom="err"): + self.nom=nom + self.definition=definition + self.valeur=valeur + self.parent=parent + self.mc_liste=[] + if parent : + self.jdc = self.parent.jdc + #self.niveau = self.parent.niveau + #self.etape = self.parent.etape + else: + # Pas de parent + self.jdc = None + #self.niveau = None + #self.etape = None + def isvalid(self,cr='non'): + return 0 + + def report(self): + """ génère le rapport de validation de self """ + self.cr=CR() + self.cr.debut = "Mot-clé invalide : "+self.nom + self.cr.fin = "Fin Mot-clé invalide : "+self.nom + self.cr.fatal("Type non autorisé pour le mot-clé %s : '%s'" % (self.nom,self.valeur)) + return self.cr + diff --git a/Noyau/N_SENSIBILITE.py b/Noyau/N_SENSIBILITE.py new file mode 100644 index 00000000..ba405c00 --- /dev/null +++ b/Noyau/N_SENSIBILITE.py @@ -0,0 +1,180 @@ +#@ MODIF N_SENSIBILITE Noyau DATE 10/05/2006 AUTEUR MCOURTOI M.COURTOIS +# -*- coding: iso-8859-1 -*- +# CONFIGURATION MANAGEMENT OF EDF VERSION +# ====================================================================== +# COPYRIGHT (C) 1991 - 2006 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. +# ====================================================================== + +""" + Ce module contient les règles nécessaires aux commandes sensibles + pour renseigner l'attribut etape.sd.sensi, gérer le caractère réentrant + sur présence de la sensibilité. +""" + +from types import TupleType, ListType +EnumTypes = (TupleType, ListType) + +from N_REGLE import REGLE + +# ----------------------------------------------------------------------------- +class CONCEPT_SENSIBLE(REGLE): + """Règle permettant de renseigner au niveau du catalogue comment sera + rempli le concept (valeur nominale ou dérivée(s) ou les deux...). + """ + def __init__(self, mode, mocle='SENSIBILITE'): + """Constructeur. + mode : manière dont la commande rempli le concept + 'ENSEMBLE' : concept nominal ET dérivées en une seule passe + 'SEPARE' : concept nominal OU dérivée (une ou plusieurs) + mocle : mot-clé contenant les paramètres sensibles. + """ + REGLE.__init__(self) + self.mocle = mocle + self._modes = { 'ENSEMBLE' : 0, 'SEPARE' : 1 } + self.mode = self._modes.get(mode, self._modes['ENSEMBLE']) + + def gettext(self): + """Pour EFICAS + """ + return '' + + def verif(self, args): + """Retourne texte + 1 si ok, 0 si nook. + On stocke dans sd.sensi l'étape courante, c'est-à-dire celle qui + renseigne le concept si cela n'a pas déjà été fait (car verif est + appelé à chaque validation). + """ + obj = args["self"] + etape = obj.etape + id_etape = '%s_%s' % (etape.id, id(etape)) + if etape.sd == None: + return '',1 + if not hasattr(etape.sd,"sensi"): + etape.sd.sensi = {} + # si ENSEMBLE, la sd nominale est forcément produite + if self.mode == self._modes['ENSEMBLE'] and not etape.sd.sensi.has_key('nominal'): + etape.sd.sensi['nominal'] = id_etape + # liste des paramètres sensibles + valeur = obj[self.mocle] + if valeur == None: + # pas de sensibilité, la sd nominale est produite + if not etape.sd.sensi.has_key('nominal'): + etape.sd.sensi['nominal'] = id_etape + return '', 1 + if not type(valeur) in EnumTypes: + valeur = [valeur,] + for v in valeur: + if not etape.sd.sensi.has_key(v.get_name()): + etape.sd.sensi[v.get_name()] = id_etape + return '', 1 + + +# ----------------------------------------------------------------------------- +class REUSE_SENSIBLE(REGLE): + """Limite le caractère réentrant de la commande. + On autorisera reuse seulement si le concept (au sens fortran) n'a pas déjà + été calculé (d'après sd.sensi). Ce sera interdit dans les cas suivants : + - sd nominale calculée et SENSIBILITE absent + - PS1 dans SENSIBILITE et sd dérivée par rapport à PS1 calculée + """ + def __init__(self, mocle='SENSIBILITE'): + """Constructeur. + mocle : mot-clé SENSIBILITE. + """ + REGLE.__init__(self) + self.mocle = mocle + + def gettext(self): + """Pour EFICAS + """ + return '' + + def verif(self,args): + """Retourne texte + 1 si ok, 0 si nook = reuse interdit. + Comme CONCEPT_SENSIBLE est appelé avant (et à chaque validation), + on regarde si sd.sensi[ps] a été renseigné par une étape précédente. + """ + obj = args["self"] + etape = obj.etape + id_etape = '%s_%s' % (etape.id, id(etape)) + sd = etape.sd + # si la commande n'est pas réentrante, rien à faire + if etape.reuse is not None: + valeur = obj[self.mocle] + if valeur is None: + if not hasattr(sd, 'sensi') or sd.sensi.get('nominal', id_etape) != id_etape: + # pas de sensibilite et concept nominal déjà calculé : reuse interdit + text = "Commande non réentrante en l'absence de sensibilité." + return text, 0 + else: + if not type(valeur) in EnumTypes: + valeur = [valeur,] + for ps in valeur: + if hasattr(sd, 'sensi') and sd.sensi.get(ps.nom, id_etape) != id_etape: + # concept dérivé par rapport à ps déjà calculé : reuse interdit + text = "Commande non réentrante : dérivée par rapport à %s déjà calculée" % ps.nom + return text, 0 + return '', 1 + + +# ----------------------------------------------------------------------------- +class DERIVABLE(REGLE): + """Déclare que le concept fourni derrière un mot-clé est dérivable. + Sa présence ne suffit pas à le valider, il faut encore que son attribut + '.sensi' soit cohérent avec le contenu du mot-clé SENSIBILITE (ou l'absence + de celui-ci). + """ + def __init__(self, mocle): + """Constructeur. + mocle : mot-clé dérivable. + """ + REGLE.__init__(self) + self.mocle = mocle + + def gettext(self): + """Pour EFICAS + """ + return '' + + def verif(self,args): + """ + """ + obj = args["self"] + try: + concept = obj[self.mocle] + except IndexError: + return '', 1 + if not type(concept) in EnumTypes: + concept = [concept,] + l_ps = obj["SENSIBILITE"] + for co in concept: + if not l_ps: + # pas de sensibilité + if hasattr(co,"sensi") and not co.sensi.get('nominal'): + text = "%s ne contient que des valeurs dérivées, utilisez le mot cle SENSIBILITE" %\ + co.nom + return text, 0 + else: + # sensibilité spécifiée + if not type(l_ps) in EnumTypes: + l_ps = [l_ps,] + for ps in l_ps: + if not hasattr(co,"sensi") or not co.sensi.get(ps.nom): + text = "La dérivée de %s par rapport à %s n'est pas disponible." %\ + (co.nom, ps.nom) + return text, 0 + return '', 1 + diff --git a/Noyau/N_VALIDATOR.py b/Noyau/N_VALIDATOR.py index d4fe3916..8781509e 100644 --- a/Noyau/N_VALIDATOR.py +++ b/Noyau/N_VALIDATOR.py @@ -1,4 +1,4 @@ -#@ MODIF N_VALIDATOR Noyau DATE 20/09/2004 AUTEUR DURAND C.DURAND +#@ MODIF N_VALIDATOR Noyau DATE 16/05/2006 AUTEUR DURAND C.DURAND # -*- coding: iso-8859-1 -*- # CONFIGURATION MANAGEMENT OF EDF VERSION # ====================================================================== @@ -22,12 +22,208 @@ Ce module contient toutes les classes necessaires pour implanter le concept de validateur dans Accas """ -import types,exceptions - -class ValError ( exceptions.Exception ): - pass +import types +import string +import traceback + +class ValError(Exception):pass + +def cls_mro(cls): + if hasattr(cls,"__mro__"):return cls.__mro__ + mro=[cls] + for base in cls.__bases__: + mro.extend(cls_mro(base)) + return mro + +class Protocol: + def __init__(self,name): + self.registry = {} + self.name = name + self.args={} + + def register(self, T, A): + self.registry[T] = A + + def adapt(self, obj): + # (a) verifier si l'objet peut s'adapter au protocole + adapt = getattr(obj, '__adapt__', None) + if adapt is not None: + # on demande à l'objet obj de réaliser lui-meme l'adaptation + return adapt(self) + + # (b) verifier si un adapteur est enregistré (si oui l'utiliser) + if self.registry: + for T in cls_mro(obj.__class__): + if T in self.registry: + return self.registry[T](obj,self,**self.args) + + # (c) utiliser l'adapteur par defaut + return self.default(obj,**self.args) + + def default(self,obj,**args): + raise TypeError("Can't adapt %s to %s" % + (obj.__class__.__name__, self.name)) + +class PProtocol(Protocol): + """Verificateur de protocole paramétré (classe de base)""" + #Protocole paramétré. Le registre est unique pour toutes les instances. La methode register est une methode de classe + registry={} + def __init__(self,name,**args): + self.name = name + self.args=args + def register(cls, T, A): + cls.registry[T] = A + register=classmethod(register) + +class ListProtocol(Protocol): + """Verificateur de protocole liste : convertit un objet quelconque en liste pour validation ultérieure""" + def default(self,obj): + if type(obj) == types.TupleType : + if obj[0] in ('RI','MP'): + #il s'agit d'un complexe ancienne mode. La cardinalite vaut 1 + return (obj,) + else: + return obj + elif type(obj) == types.ListType : + return obj + elif obj == None : + # pas de valeur affecte. La cardinalite vaut 0 + return obj + elif type(obj) == types.StringType : + #il s'agit d'une chaine. La cardinalite vaut 1 + return (obj,) + else: + try: + # si l'objet supporte len, on a la cardinalite + length=len(obj) + return obj + except: + # sinon elle vaut 1 + return (obj,) + +listProto=ListProtocol("list") + +class TypeProtocol(PProtocol): + """Verificateur de type parmi une liste de types possibles""" + #pas de registre par instance. Registre unique pour toutes les instances de TypeProtocol + registry={} + def __init__(self,name,typ=None): + PProtocol.__init__(self,name,typ=typ) + self.typ=typ + + def default(self,obj,typ): + for type_permis in typ: + if type_permis == 'R': + if type(obj) in (types.IntType,types.FloatType,types.LongType):return obj + elif type_permis == 'I': + if type(obj) in (types.IntType,types.LongType):return obj + elif type_permis == 'C': + if self.is_complexe(obj):return obj + elif type_permis == 'TXM': + if type(obj)==types.StringType:return obj + elif type_permis == 'shell': + if type(obj)==types.StringType:return obj + elif type(type_permis) == types.ClassType: + if self.is_object_from(obj,type_permis):return obj + elif type(type_permis) == types.InstanceType: + try: + if type_permis.__convert__(obj) : return obj + except: + pass + else: + print "Type non encore géré %s" %`type_permis` + + raise ValError("%s n'est pas d'un type autorisé: %s" % (repr(obj),typ)) + + def is_complexe(self,valeur): + """ Retourne 1 si valeur est un complexe, 0 sinon """ + if type(valeur) in (types.ComplexType,types.IntType,types.FloatType,types.LongType): + # Pour permettre l'utilisation de complexes Python + return 1 + elif type(valeur) != types.TupleType : + # On n'autorise pas les listes pour les complexes + return 0 + elif len(valeur) != 3: + return 0 + else: + # Un complexe doit etre un tuple de longueur 3 avec 'RI' ou 'MP' comme premiere + # valeur suivie de 2 reels. + if string.strip(valeur[0]) in ('RI','MP'): + try: + v1=reelProto.adapt(valeur[1]),reelProto.adapt(valeur[2]) + return 1 + except: + return 0 + else: + return 0 -class Valid: + def is_object_from(self,objet,classe): + """ + Retourne 1 si objet est une instance de la classe classe, 0 sinon + """ + convert = getattr(classe, '__convert__', None) + if convert is not None: + # classe verifie les valeurs + try: + v= convert(objet) + return v is not None + except: + return 0 + # On accepte les instances de la classe et des classes derivees + return type(objet) == types.InstanceType and isinstance(objet,classe) + +reelProto=TypeProtocol("reel",typ=('R',)) + +class CardProtocol(PProtocol): + """Verificateur de cardinalité """ + #pas de registre par instance. Registre unique pour toutes les instances + registry={} + def __init__(self,name,min=1,max=1): + PProtocol.__init__(self,name,min=min,max=max) + + def default(self,obj,min,max): + length=len(obj) + if length < min or length >max: + raise ValError("Nombre d'arguments de %s incorrect (min = %s, max = %s)" % (repr(obj),min,max) ) + return obj + +class IntoProtocol(PProtocol): + """Verificateur de choix possibles : liste discrète ou intervalle""" + #pas de registre par instance. Registre unique pour toutes les instances + registry={} + def __init__(self,name,into=None,val_min='**',val_max='**'): + PProtocol.__init__(self,name,into=into,val_min=val_min,val_max=val_max) + self.val_min=val_min + self.val_max=val_max + + def default(self,obj,into,val_min,val_max): + if into: + if obj not in into: + raise ValError("La valeur : %s ne fait pas partie des choix possibles %s" % (repr(obj),into) ) + else: + #on est dans le cas d'un ensemble continu de valeurs possibles (intervalle) + if type(obj) in (types.IntType,types.FloatType,types.LongType) : + if val_min == '**': val_min = obj -1 + if val_max == '**': val_max = obj +1 + if obj < val_min or obj > val_max : + raise ValError("La valeur : %s est en dehors du domaine de validité [ %s , %s ]" % (repr(obj),self.val_min,self.val_max) ) + return obj + +class MinStr: + #exemple de classe pour verificateur de type + #on utilise des instances de classe comme type (typ=MinStr(3,6), par exemple) + def __init__(self,min,max): + self.min=min + self.max=max + + def __convert__(self,valeur): + if type(valeur) == types.StringType and self.min <= len(valeur) <= self.max:return valeur + raise ValError("%s n'est pas une chaine de longueur comprise entre %s et %s" % (valeur,self.min,self.max)) + + def __repr__(self): + return "TXM de longueur entre %s et %s" %(self.min,self.max) + +class Valid(PProtocol): """ Cette classe est la classe mere des validateurs Accas Elle doit etre derivee @@ -37,12 +233,9 @@ class Valid: @ivar cata_info: raison de la validite ou de l'invalidite du validateur meme @type cata_info: C{string} """ - def __init__(self,*tup,**args): - """ - Cette methode sert a initialiser les attributs du validateur - """ - self.cata_info="" - raise "Must be implemented" + registry={} + def __init__(self,**args): + PProtocol.__init__(self,"valid",**args) def info(self): """ @@ -173,22 +366,6 @@ class Valid: """ return into_courant - def is_param(self,valeur): - """ - Cette méthode indique si valeur est un objet de type PARAMETRE - dont on cherchera à evaluer la valeur (valeur.valeur) - """ - #return type(valeur) == types.InstanceType and valeur.__class__.__name__ in ('PARAMETRE',) - return 0 - - def is_unknown(self,valeur): - """ - Cette méthode indique si valeur est un objet de type inconnu - c'est à dire pas de type PARAMETRE - """ - return type(valeur) == types.InstanceType and valeur.__class__.__name__ not in ( - 'entier','reel','chaine', 'complexe','liste','PARAMETRE_EVAL','PARAMETRE') - class ListVal(Valid): """ Cette classe sert de classe mère pour tous les validateurs qui acceptent @@ -212,6 +389,19 @@ class ListVal(Valid): liste_choix.append(e) return liste_choix + def convert(self,valeur): + """ + Méthode convert pour les validateurs de listes. Cette méthode + fait appel à la méthode convert_item sur chaque élément de la + liste. + """ + if type(valeur) in (types.ListType,types.TupleType): + for val in valeur: + self.convert_item(val) + return valeur + else: + return self.convert_item(valeur) + def verif(self,valeur): """ Méthode verif pour les validateurs de listes. Cette méthode @@ -219,8 +409,6 @@ class ListVal(Valid): liste. Si valeur est un paramètre, on utilise sa valeur effective valeur.valeur. """ - if self.is_param(valeur): - valeur=valeur.valeur if type(valeur) in (types.ListType,types.TupleType): for val in valeur: if not self.verif_item(val): @@ -229,164 +417,58 @@ class ListVal(Valid): else: return self.verif_item(valeur) -class RangeVal(ListVal): - """ - Exemple de classe validateur : verification qu'une valeur - est dans un intervalle. - Pour une liste on verifie que tous les elements sont - dans l'intervalle - Susceptible de remplacer les attributs "vale_min" "vale_max" - dans les catalogues - """ - def __init__(self,low,high): - self.low=low - self.high=high - self.cata_info="%s doit etre inferieur a %s" %(low,high) - - def info(self): - return "valeur dans l'intervalle %s , %s" %(self.low,self.high) - - def verif_item(self,valeur): - return valeur > self.low and valeur < self.high - - def info_erreur_item(self) : - return "La valeur doit etre comprise entre %s et %s" % (self.low, - self.high) - - def verif_cata(self): - if self.low > self.high : return 0 - return 1 - -class CardVal(Valid): +class Compulsory(ListVal): """ - Exemple de classe validateur : verification qu'une liste est - d'une longueur superieur a un minimum (min) et inferieure - a un maximum (max). - Susceptible de remplacer les attributs "min" "max" dans les - catalogues + Validateur operationnel + Verification de la présence obligatoire d'un élément dans une liste """ - def __init__(self,min='**',max='**'): - self.min=min - self.max=max - self.cata_info="%s doit etre inferieur a %s" % (min,max) + registry={} + def __init__(self,elem=()): + if type(elem) not in (types.ListType,types.TupleType): elem=(elem,) + Valid.__init__(self,elem=elem) + self.elem=elem + self.cata_info="" def info(self): - return "longueur de liste comprise entre %s et %s" % (self.min,self.max) - - def info_erreur_liste(self): - return "Le cardinal de la liste doit etre compris entre %s et %s" % (self.min,self.max) + return "valeur %s obligatoire" % `self.elem` - def is_list(self): - return self.max == '**' or self.max > 1 - - def get_into(self,liste_courante=None,into_courant=None): - if into_courant is None: - return None - elif liste_courante is None: - return into_courant - elif self.max == '**': - return into_courant - elif len(liste_courante) < self.max: - return into_courant - else: - return [] + def default(self,valeur,elem): + return valeur def verif_item(self,valeur): return 1 - def verif(self,valeur): - if type(valeur) in (types.ListType,types.TupleType): - if self.max != '**' and len(valeur) > self.max:return 0 - if self.min != '**' and len(valeur) < self.min:return 0 - return 1 - else: - if self.max != '**' and 1 > self.max:return 0 - if self.min != '**' and 1 < self.min:return 0 - return 1 + def convert(self,valeur): + elem=list(self.elem) + for val in valeur: + v=self.adapt(val) + if v in elem:elem.remove(v) + if elem: + raise ValError("%s ne contient pas les elements obligatoires : %s " %(valeur,elem)) + return valeur - def verif_cata(self): - if self.min != '**' and self.max != '**' and self.min > self.max : return 0 + def has_into(self): return 1 - def valide_liste_partielle(self,liste_courante=None): - validite=1 - if liste_courante != None : - if len(liste_courante) > self.max : - validite=0 - return validite - -class PairVal(ListVal): - """ - Exemple de classe validateur : verification qu'une valeur - est paire. - Pour une liste on verifie que tous les elements sont - pairs - """ - def __init__(self): - self.cata_info="" - - def info(self): - return "valeur paire" - - def info_erreur_item(self): - return "La valeur saisie doit etre paire" - - def verif_item(self,valeur): - if type(valeur) == types.InstanceType: - if self.is_param(valeur): - valeur=valeur.valeur - else: - return 0 - return valeur % 2 == 0 - def verif(self,valeur): - if type(valeur) in (types.ListType,types.TupleType): - for val in valeur: - if val % 2 != 0:return 0 - return 1 + if type(valeur) not in (types.ListType,types.TupleType): + liste=list(valeur) else: - if valeur % 2 != 0:return 0 - return 1 - -class EnumVal(ListVal): - """ - Exemple de classe validateur : verification qu'une valeur - est prise dans une liste de valeurs. - Susceptible de remplacer l attribut "into" dans les catalogues - """ - def __init__(self,into=()): - if type(into) not in (types.ListType,types.TupleType): into=(into,) - self.into=into - self.cata_info="" - - def info(self): - return "valeur dans %s" % `self.into` - - def verif_item(self,valeur): - if valeur not in self.into:return 0 - return 1 - - def has_into(self): + liste=valeur + for val in self.elem : + if val not in liste : return 0 return 1 - def get_into(self,liste_courante=None,into_courant=None): - if into_courant is None: - liste_choix= list(self.into) - else: - liste_choix=[] - for e in into_courant: - if e in self.into: - liste_choix.append(e) - return liste_choix - def info_erreur_item(self): return "La valeur n'est pas dans la liste des choix possibles" class NoRepeat(ListVal): """ + Validateur operationnel Verification d'absence de doublons dans la liste. """ def __init__(self): + Valid.__init__(self) self.cata_info="" def info(self): @@ -395,6 +477,17 @@ class NoRepeat(ListVal): def info_erreur_liste(self): return "Les doublons ne sont pas permis" + def default(self,valeur): + if valeur in self.liste : raise ValError("%s est un doublon" % valeur) + return valeur + + def convert(self,valeur): + self.liste=[] + for val in valeur: + v=self.adapt(val) + self.liste.append(v) + return valeur + def verif_item(self,valeur): return 1 @@ -425,9 +518,11 @@ class NoRepeat(ListVal): class LongStr(ListVal): """ + Validateur operationnel Verification de la longueur d une chaine """ def __init__(self,low,high): + ListVal.__init__(self,low=low,high=high) self.low=low self.high=high self.cata_info="" @@ -438,21 +533,35 @@ class LongStr(ListVal): def info_erreur_item(self): return "Longueur de la chaine incorrecte" + def convert(self,valeur): + for val in valeur: + v=self.adapt(val) + return valeur + def verif_item(self,valeur): - low=self.low - high=self.high + try: + self.adapt(valeur) + return 1 + except: + return 0 + + def default(self,valeur,low,high): + if type(valeur) != types.StringType : + raise ValError("%s n'est pas une string" % repr(valeur)) if valeur[0]=="'" and valeur[-1]=="'" : low=low+2 high=high+2 - if len(valeur) < low :return 0 - if len(valeur) > high:return 0 - return 1 + if len(valeur) < low or len(valeur) > high : + raise ValError("%s n'est pas de la bonne longueur" % repr(valeur)) + return valeur class OrdList(ListVal): """ + Validateur operationnel Verification qu'une liste est croissante ou decroissante """ def __init__(self,ord): + ListVal.__init__(self,ord=ord) self.ord=ord self.cata_info="" @@ -462,22 +571,22 @@ class OrdList(ListVal): def info_erreur_liste(self) : return "La liste doit etre en ordre "+self.ord - def verif(self,valeur): - if type(valeur) in (types.ListType,types.TupleType): - if self.ord=='croissant': - var=valeur[0] - for val in valeur[1:]: - if valvar:return 0 - var=val - return 1 - else: - return 1 + def convert(self,valeur): + self.val=None + self.liste=valeur + for v in valeur: + self.adapt(v) + return valeur + + def default(self,valeur,ord): + if self.ord=='croissant': + if self.val is not None and valeur self.val: + raise ValError("%s n'est pas par valeurs decroissantes" % repr(self.liste)) + self.val=valeur + return valeur def verif_item(self,valeur): return 1 @@ -502,160 +611,61 @@ class OrdList(ListVal): liste_choix.append(e) return liste_choix -CoercableFuncs = { types.IntType: int, - types.LongType: long, - types.FloatType: float, - types.ComplexType: complex, - types.UnicodeType: unicode } - -class TypeVal(ListVal): +class OrVal(Valid): """ - Cette classe est un validateur qui controle qu'une valeur - est bien du type Python attendu. - Pour une liste on verifie que tous les elements sont du bon type. + Validateur operationnel + Cette classe est un validateur qui controle une liste de validateurs + Elle verifie qu'au moins un des validateurs de la liste valide la valeur """ - def __init__(self, aType): - if type(aType) != types.TypeType: - aType=type(aType) - self.aType=aType - try: - self.coerce=CoercableFuncs[ aType ] - except: - self.coerce = self.identity + def __init__(self,validators=()): + if type(validators) not in (types.ListType,types.TupleType): + validators=(validators,) + self.validators=[] + for validator in validators: + if type(validator) == types.FunctionType: + self.validators.append(FunctionVal(validator)) + else: + self.validators.append(validator) + self.cata_info="" def info(self): - return "valeur de %s" % self.aType - - def identity ( self, value ): - if type( value ) == self.aType: - return value - raise ValError + return "\n ou ".join([v.info() for v in self.validators]) - def verif_item(self,valeur): - try: - self.coerce(valeur) - except: - return 0 - return 1 + def convert(self,valeur): + for validator in self.validators: + try: + return validator.convert(valeur) + except: + pass + raise ValError("%s n'est pas du bon type" % repr(valeur)) -class InstanceVal(ListVal): - """ - Cette classe est un validateur qui controle qu'une valeur est - bien une instance (au sens Python) d'une classe - Pour une liste on verifie chaque element de la liste - """ - def __init__(self,aClass): - if type(aClass) == types.InstanceType: - aClass=aClass.__class__ - self.aClass=aClass + def info_erreur_item(self): + l=[] + for v in self.validators: + err=v.info_erreur_item() + if err != " " : l.append(err) + chaine=" \n ou ".join(l) + return chaine - def info(self): - return "valeur d'instance de %s" % self.aClass.__name__ + def info_erreur_liste(self): + l=[] + for v in self.validators: + err=v.info_erreur_liste() + if err != " " : l.append(err) + chaine=" \n ou ".join(l) + return chaine - def verif_item(self,valeur): - if not isinstance(valeur,self.aClass): return 0 - return 1 - -def ImpairVal(valeur): - """ - Cette fonction est un validateur. Elle verifie que la valeur passee - est bien un nombre impair. - """ - if type(valeur) in (types.ListType,types.TupleType): - for val in valeur: - if val % 2 != 1:return 0 - return 1 - else: - if valeur % 2 != 1:return 0 - return 1 - -ImpairVal.info="valeur impaire" - -class F1Val(Valid): - """ - Cette classe est un validateur de dictionnaire (mot cle facteur ?). Elle verifie - que la somme des cles A et B vaut une valeur donnee - en parametre du validateur - """ - def __init__(self,somme=10): - self.somme=somme - self.cata_info="" - - def info(self): - return "valeur %s pour la somme des cles A et B " % self.somme - - def verif(self,valeur): - if type(valeur) in (types.ListType,types.TupleType): - for val in valeur: - if not val.has_key("A"):return 0 - if not val.has_key("B"):return 0 - if val["A"]+val["B"] != self.somme:return 0 - return 1 - else: - if not valeur.has_key("A"):return 0 - if not valeur.has_key("B"):return 0 - if valeur["A"]+valeur["B"] != self.somme:return 0 - return 1 - -class FunctionVal(Valid): - """ - Cette classe est un validateur qui est initialise avec une fonction - """ - def __init__(self,function): - self.function=function - - def info(self): - return self.function.info - - def verif(self,valeur): - return self.function(valeur) - -class OrVal(Valid): - """ - Cette classe est un validateur qui controle une liste de validateurs - Elle verifie qu'au moins un des validateurs de la liste valide la valeur - """ - def __init__(self,validators=()): - if type(validators) not in (types.ListType,types.TupleType): - validators=(validators,) - self.validators=[] - for validator in validators: - if type(validator) == types.FunctionType: - self.validators.append(FunctionVal(validator)) - else: - self.validators.append(validator) - self.cata_info="" - - def info(self): - return "\n ou ".join([v.info() for v in self.validators]) - - def info_erreur_item(self): - l=[] - for v in self.validators: - err=v.info_erreur_item() - if err != " " : l.append(err) - chaine=" \n ou ".join(l) - return chaine - - def info_erreur_liste(self): - l=[] - for v in self.validators: - err=v.info_erreur_liste() - if err != " " : l.append(err) - chaine=" \n ou ".join(l) - return chaine - - def is_list(self): - """ - Si plusieurs validateurs sont reliés par un OU - il suffit qu'un seul des validateurs attende une liste - pour qu'on considère que leur union attend une liste. - """ - for validator in self.validators: - v=validator.is_list() - if v : - return 1 - return 0 + def is_list(self): + """ + Si plusieurs validateurs sont reliés par un OU + il suffit qu'un seul des validateurs attende une liste + pour qu'on considère que leur union attend une liste. + """ + for validator in self.validators: + v=validator.is_list() + if v : + return 1 + return 0 def verif(self,valeur): for validator in self.validators: @@ -728,11 +738,12 @@ class OrVal(Valid): class AndVal(Valid): """ + Validateur operationnel Cette classe est un validateur qui controle une liste de validateurs Elle verifie que tous les validateurs de la liste valident la valeur """ def __init__(self,validators=()): - if type(validators) not in (types.ListType,types.TupleType): + if type(validators) not in (types.ListType,types.TupleType): validators=(validators,) self.validators=[] for validator in validators: @@ -745,6 +756,11 @@ class AndVal(Valid): def info(self): return "\n et ".join([v.info() for v in self.validators]) + def convert(self,valeur): + for validator in self.validators: + valeur=validator.convert(valeur) + return valeur + def info_erreur_item(self): chaine="" a=1 @@ -878,3 +894,301 @@ def validatorFactory(validator): return AndVal(do_liste(validator)) else: return validator + +# Ci-dessous : exemples de validateur (peu testés) + +class RangeVal(ListVal): + """ + Exemple de classe validateur : verification qu'une valeur + est dans un intervalle. + Pour une liste on verifie que tous les elements sont + dans l'intervalle + Susceptible de remplacer les attributs "vale_min" "vale_max" + dans les catalogues + """ + def __init__(self,low,high): + self.low=low + self.high=high + self.cata_info="%s doit etre inferieur a %s" %(low,high) + + def info(self): + return "valeur dans l'intervalle %s , %s" %(self.low,self.high) + + def convert_item(self,valeur): + if valeur > self.low and valeur < self.high:return valeur + raise ValError("%s devrait etre comprise entre %s et %s" %(valeur,self.low,self.high)) + + def verif_item(self,valeur): + return valeur > self.low and valeur < self.high + + def info_erreur_item(self) : + return "La valeur doit etre comprise entre %s et %s" % (self.low, + self.high) + + def verif_cata(self): + if self.low > self.high : return 0 + return 1 + +class CardVal(Valid): + """ + Exemple de classe validateur : verification qu'une liste est + d'une longueur superieur a un minimum (min) et inferieure + a un maximum (max). + Susceptible de remplacer les attributs "min" "max" dans les + catalogues + """ + def __init__(self,min='**',max='**'): + self.min=min + self.max=max + self.cata_info="%s doit etre inferieur a %s" % (min,max) + + def info(self): + return "longueur de liste comprise entre %s et %s" % (self.min,self.max) + + def info_erreur_liste(self): + return "Le cardinal de la liste doit etre compris entre %s et %s" % (self.min,self.max) + + def is_list(self): + return self.max == '**' or self.max > 1 + + def get_into(self,liste_courante=None,into_courant=None): + if into_courant is None: + return None + elif liste_courante is None: + return into_courant + elif self.max == '**': + return into_courant + elif len(liste_courante) < self.max: + return into_courant + else: + return [] + + def convert(self,valeur): + if type(valeur) in (types.ListType,types.TupleType): + l=len(valeur) + elif valeur is None: + l=0 + else: + l=1 + if self.max != '**' and l > self.max:raise ValError("%s devrait etre de longueur inferieure a %s" %(valeur,self.max)) + if self.min != '**' and l < self.min:raise ValError("%s devrait etre de longueur superieure a %s" %(valeur,self.min)) + return valeur + + def verif_item(self,valeur): + return 1 + + def verif(self,valeur): + if type(valeur) in (types.ListType,types.TupleType): + if self.max != '**' and len(valeur) > self.max:return 0 + if self.min != '**' and len(valeur) < self.min:return 0 + return 1 + else: + if self.max != '**' and 1 > self.max:return 0 + if self.min != '**' and 1 < self.min:return 0 + return 1 + + def verif_cata(self): + if self.min != '**' and self.max != '**' and self.min > self.max : return 0 + return 1 + + def valide_liste_partielle(self,liste_courante=None): + validite=1 + if liste_courante != None : + if len(liste_courante) > self.max : + validite=0 + return validite + +class PairVal(ListVal): + """ + Exemple de classe validateur : verification qu'une valeur + est paire. + Pour une liste on verifie que tous les elements sont + pairs + """ + def __init__(self): + ListVal.__init__(self) + self.cata_info="" + + def info(self): + return "valeur paire" + + def info_erreur_item(self): + return "La valeur saisie doit etre paire" + + def convert(self,valeur): + for val in valeur: + v=self.adapt(val) + if v % 2 != 0:raise ValError("%s contient des valeurs non paires" % repr(valeur)) + return valeur + + def default(self,valeur): + return valeur + + def verif_item(self,valeur): + if type(valeur) == types.InstanceType: + return 0 + return valeur % 2 == 0 + + def verif(self,valeur): + if type(valeur) in (types.ListType,types.TupleType): + for val in valeur: + if val % 2 != 0:return 0 + return 1 + else: + if valeur % 2 != 0:return 0 + return 1 + +class EnumVal(ListVal): + """ + Exemple de classe validateur : verification qu'une valeur + est prise dans une liste de valeurs. + Susceptible de remplacer l attribut "into" dans les catalogues + """ + def __init__(self,into=()): + if type(into) not in (types.ListType,types.TupleType): into=(into,) + self.into=into + self.cata_info="" + + def info(self): + return "valeur dans %s" % `self.into` + + def convert_item(self,valeur): + if valeur in self.into:return valeur + raise ValError("%s contient des valeurs hors des choix possibles: %s " %(valeur,self.into)) + + def verif_item(self,valeur): + if valeur not in self.into:return 0 + return 1 + + def has_into(self): + return 1 + + def get_into(self,liste_courante=None,into_courant=None): + if into_courant is None: + liste_choix= list(self.into) + else: + liste_choix=[] + for e in into_courant: + if e in self.into: + liste_choix.append(e) + return liste_choix + + def info_erreur_item(self): + return "La valeur n'est pas dans la liste des choix possibles" + +def ImpairVal(valeur): + """ + Exemple de validateur + Cette fonction est un validateur. Elle verifie que la valeur passee + est bien un nombre impair. + """ + if type(valeur) in (types.ListType,types.TupleType): + for val in valeur: + if val % 2 != 1:return 0 + return 1 + else: + if valeur % 2 != 1:return 0 + return 1 + +ImpairVal.info="valeur impaire" + +class F1Val(Valid): + """ + Exemple de validateur + Cette classe est un validateur de dictionnaire (mot cle facteur ?). Elle verifie + que la somme des cles A et B vaut une valeur donnee + en parametre du validateur + """ + def __init__(self,somme=10): + self.somme=somme + self.cata_info="" + + def info(self): + return "valeur %s pour la somme des cles A et B " % self.somme + + def verif(self,valeur): + if type(valeur) in (types.ListType,types.TupleType): + for val in valeur: + if not val.has_key("A"):return 0 + if not val.has_key("B"):return 0 + if val["A"]+val["B"] != self.somme:return 0 + return 1 + else: + if not valeur.has_key("A"):return 0 + if not valeur.has_key("B"):return 0 + if valeur["A"]+valeur["B"] != self.somme:return 0 + return 1 + +class FunctionVal(Valid): + """ + Exemple de validateur + Cette classe est un validateur qui est initialise avec une fonction + """ + def __init__(self,function): + self.function=function + + def info(self): + return self.function.info + + def verif(self,valeur): + return self.function(valeur) + +CoercableFuncs = { types.IntType: int, + types.LongType: long, + types.FloatType: float, + types.ComplexType: complex, + types.UnicodeType: unicode } + +class TypeVal(ListVal): + """ + Exemple de validateur + Cette classe est un validateur qui controle qu'une valeur + est bien du type Python attendu. + Pour une liste on verifie que tous les elements sont du bon type. + """ + def __init__(self, aType): + if type(aType) != types.TypeType: + aType=type(aType) + self.aType=aType + try: + self.coerce=CoercableFuncs[ aType ] + except: + self.coerce = self.identity + + def info(self): + return "valeur de %s" % self.aType + + def identity ( self, value ): + if type( value ) == self.aType: + return value + raise ValError + + def convert_item(self,valeur): + return self.coerce(valeur) + + def verif_item(self,valeur): + try: + self.coerce(valeur) + except: + return 0 + return 1 + +class InstanceVal(ListVal): + """ + Exemple de validateur + Cette classe est un validateur qui controle qu'une valeur est + bien une instance (au sens Python) d'une classe + Pour une liste on verifie chaque element de la liste + """ + def __init__(self,aClass): + if type(aClass) == types.InstanceType: + aClass=aClass.__class__ + self.aClass=aClass + + def info(self): + return "valeur d'instance de %s" % self.aClass.__name__ + + def verif_item(self,valeur): + if not isinstance(valeur,self.aClass): return 0 + return 1 + diff --git a/Tests/HTMLTestRunner.py b/Tests/HTMLTestRunner.py index e59a8efa..8da592f2 100644 --- a/Tests/HTMLTestRunner.py +++ b/Tests/HTMLTestRunner.py @@ -1,5 +1,41 @@ """ -Copyright (c) 2004, Wai Yip Tung +A TestRunner for use with the Python unit testing framework. It +generates a HTML report to show the result at a glance. + +The simplest way to use this is to invoke its main method. E.g. + + import unittest + import HTMLTestRunner + + ... define your tests ... + + if __name__ == '__main__': + HTMLTestRunner.main() + + +To customize the report, instantiates a HTMLTestRunner object and set +the parameters. HTMLTestRunner is a counterpart to unittest's +TextTestRunner. E.g. + + # output to a file + fp = file('my_report.html', 'wb') + runner = HTMLTestRunner.HTMLTestRunner( + stream=fp, + title='My unit test', + report_attrs=[('Version','1.2.3')], + description='This demonstrates the report output by HTMLTestRunner.' + ) + + # Use an external stylesheet. + # See the Template_mixin class for more customizable options + runner.STYLESHEET_TMPL = '' + + # run the test + runner.run(my_test_suite) + + +------------------------------------------------------------------------ +Copyright (c) 2004-2006, Wai Yip Tung All rights reserved. Redistribution and use in source and binary forms, with or without @@ -11,9 +47,9 @@ met: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -* Neither the name of the Wai Yip Tung nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. +* Neither the name Wai Yip Tung nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -26,38 +62,28 @@ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" -A TestRunner for use with the Python unit testing framework. It -generates a HTML report to show the result at a glance. - -The simplest way to use this is to invoke its main method. E.g. - - import unittest - import HTMLTestRunner +# URL: http://tungwaiyip.info/software/HTMLTestRunner.html - ... define your tests ... +__author__ = "Wai Yip Tung" +__version__ = "0.8.0" - if __name__ == '__main__': - HTMLTestRunner.main() -It defines the class HTMLTestRunner, which is a counterpart of unittest's -TextTestRunner. You can also instantiates a HTMLTestRunner object for -finer control. """ +Changes in 0.8.0 +* Define Template_mixin class for customization. +* Workaround a IE 6 bug that it does not treat -

$description

-

Time: $time

-

Status: $status

+%(heading)s +%(report)s +%(ending)s + + + +""" + # variables: (title, generator, stylesheet, heading, report, ending) + + + # ------------------------------------------------------------------------ + # Stylesheet + # + # alternatively use a for external style sheet, e.g. + # + + STYLESHEET_TMPL = """ + +""" + + + + # ------------------------------------------------------------------------ + # Heading + # + + HEADING_TMPL = """
+

%(title)s

+%(parameters)s +

%(description)s

+
+ +""" # variables: (title, parameters, description) + + HEADING_ATTRIBUTE_TMPL = """

%(name)s: %(value)s

+""" # variables: (name, value) + + + + # ------------------------------------------------------------------------ + # Report + # + + REPORT_TMPL = """

Show Summary Failed @@ -254,52 +385,76 @@ function showOutput(id, name) { Error View -$tests +%(test_list)s Total - $count - $Pass - $fail - $error + %(count)s + %(Pass)s + %(fail)s + %(error)s   -

- - -""") - -CLASS_TMPL = string.Template(r""" - - $name - $count - $Pass - $fail - $error - Detail +""" # variables: (test_list, count, Pass, fail, error) + + + REPORT_CLASS_TMPL = r""" + + %(name)s + %(count)s + %(Pass)s + %(fail)s + %(error)s + Detail -""") +""" # variables: (style, name, count, Pass, fail, error, cid) + -TEST_TMPL = string.Template(r""" - -
$name
- $status + REPORT_TEST_WITH_OUTPUT_TMPL = r""" + +
%(name)s
+ %(status)s -""") +""" # variables: (tid, Class, style, name, status) + -TEST_TMPL_NO_OUTPUT = string.Template(r""" - -
$name
- $status + REPORT_TEST_NO_OUTPUT_TMPL = r""" + +
%(name)s
+ %(status)s -""") +""" # variables: (tid, Class, style, name, status) -TEST_OUTPUT_TMPL = string.Template(r""" - -""") + REPORT_TEST_OUTPUT_TMPL = r""" + +""" # variables: (id, output) + + + + # ------------------------------------------------------------------------ + # ENDING + # + + ENDING_TMPL = """
 
""" + +# -------------------- The end of the Template class ------------------- + + + +def jsEscapeString(s): + """ Escape s for use as a Javascript String """ + return s.replace('\\','\\\\') \ + .replace('\r', '\\r') \ + .replace('\n', '\\n') \ + .replace('"', '\\"') \ + .replace("'", "\\'") \ + .replace("&", '\\x26') \ + .replace("<", '\\x3C') \ + .replace(">", '\\x3E') + # Note: non-ascii unicode characters do not need to be encoded + # Note: previously we encode < as <, etc. However IE6 fail to treat