mardi 29 décembre 2009

Les partitions gratuites de Marc Lambert pour guitare

Marc Lambert  publie 90 Partitions gratuites pour guitare, toutes classées par niveau de difficulté.
"Marc sa guitare" accessible sur guitare.org publie aussi des partitions pour groupe de trois ou quatre guitares.

En guise d'exemple, voici une interprétation  de la "Valse Bleue" de Marc Lambert.
Simplement magnifique!


mercredi 23 décembre 2009

Utilisation des blogs pour consolider les connaissances d'entreprise

L'utilisation et la tenue d'un blog est souvent considéré comme une perte de temps par le monde du travail.
Dans le cadre professionnel, il existe cependant une utilisation rentable de ces blogs
 

L'avis personnel
L'expérience de cette dernière année avec "Developer Notepad" (ce blog) a été professionnellement payant plus d'une fois. Une simple recherche Google fournissant une réponse aux questions déjà abordées et élucidées.
L'utilisation d'un blog de type professionnel pour organiser et maintenir des connaissances techniques est devenu pour moi une évidence en soi.
A mes yeux, le retour sur investissement (le temps nécessaire à la rédaction) ne fait plus de doute et est parfaitement démontrable.
Je dois avouer que mon expérience recoupe parfaitement les conclusions exposées dans l'article de Jean-Luc Tholozan (voir "un retour d'expérience" ci-dessous).
Article que je vous invite par ailleurs à parcourir.


Un retour d'expérience
Allant plus loin dans l'étude d'utilisation d'un Blog, Jean-Luc Tholozan a écrit l'article "Entreprise 2.0 : Blogging en R&D, Un retour d'expérience".
Dans ce dernier, il partage son expérience et ses conclusions sur l'utilisation des technologies WEB 2.0 (et donc aussi les blogs) "dans le cadre d'une étude sur l'utilisation de ces technologies à la gestion des connaissances dans le monde de l'entreprise" (dixit Jean-Luc Tholozan).

Cet article très bien construit démontre l'utilité, la pertinence et les avantages de l'utilisation d'un Blog publique dans un service de Recherche et Développement.
J'ai également été impressionné par l'approche concernant la pérénité et la consolidation des informations acquises par le service de R&D de son entreprise.

Voici un extrait de de l'article "Entreprise 2.0 : Blogging en R&D, Un retour d'expérience" publié sur Architecture des S.I.
Une seconde raison de l'utilisation du Blog est qu'il correspond bien à la problématique de publication d'une équipe de R&D : les phases de recherche s'appuient sur des études bibliographiques importantes. On a très vite tendance à lire et oublier pour passer rapidement à l'étape suivante. Les étapes intermédiaires disparaissent plus ou moins rapidement de la mémoire du chercheur, mais n'entre jamais dans le patrimoine de connaissance de l'entreprise. La publication sous forme de Blog permet d'alterner les phases de lectures et celles de publications : ces dernières permettent à la fois la consolidation des connaissances de l'individu chercheur mais également la consolidation des connaissances dans l'entreprise.
Avec ses conclusions
La création de Blog personnel pour une équipe de R&D s'avère très rentable pour l'entreprise :
  • le suivi de la R&D est facilité
  • la productivité n'est pas pénalisée : l'effort supplémentaire est compensé par les gains qui suivent
  • le patrimoine de connaissance de l'entreprise est consolidé

Un article de grande qualité et pertinent.
Un incontournable à lire!

Sources:

mardi 22 décembre 2009

Isodore de Séville, le saint patron des informaticiens

Fêté le 4 avril, Isodore de Séville à été proposé comme Saint patron des informaticiens (des utilisateurs d'ordinateurs et d'internet par extension).

Ce qui vaut à Saint Isidore de devenir notre potentiel Saint patron, c'est la création et l'organisation d'un bestiaire médiévale pour l'un des 26 volumes constituant son oeuvre relative à l'étymologique.
En agissant de la sorte, Isodore fût un précurseur en créant une table des matières qui sert à l'indexation du contenu (du moins assimilé comme). Ces mêmes indexes, ordonnancements alphabétiques, que l'univers de l'information (et donc les informaticiens) utilise à outrance.

Isidore de Séville, né entre 560 et 570 à Carthagène (Espagne) – mort le 4 avril 636, est un religieux espagnol du VIIe siècle. Il a été canonisé en 1598 et est fêté chaque année le 4 avril (Isidore de Séville sur Wikipédia).

Bref, lors du prochain plantage ou bug, je saurai enfin quel Saint invoquer :-)

Un meilleur éditeur pour Blogger

Petite mise-à-jour ce 22 décembre pour référencer "Windows Live Writer" et "ScribeFire" 

Blogger dispose d'une interface d'édition nettement plus efficace et conviviale que l'interface actuelle.
Cette nouvelle interface est accessible via le site "laboratoire" draft.blogger.com .
Je dois reconnaître que c'est une version vraiment pratique et surtout stable.
Les fonctionnalités de draft.blogger.com sont décrites ici sur le blog de Roberto.

Il est possible de tester l'éditeur directement depuis draft.blogger.com.
S'il convient, activer l'option "Faire de Blogger in Draft mon tableau de bord par défaut" dans le tableau de bord. Il sera ainsi possible de profiter de ce nouvel éditeur directement depuis les pages du blog.

Windows Live Writer
Pour les habitués de Microsoft, il semblerait qu'il existe un produit gratuit nommé "Windows Live Writer" (WLW).
Toujours en se fiant à l'article de Jean-Luc Tholozan, ce logiciel est compatible avec Blogger et offre une confort d'utilisation de loin bien plus agréable que l'éditeur en ligne de Blogger.
WLW supporte le copier/coller avec Word sans importer/générer de code html parasite (véritable plaie du support HTML de Word). WLW se présente là comme un excellent intermédiaire d'autant plus qu'il travaille en mode déconnecté. Finalement, et non des moindres, WLW gère parfaitement les caratères ">" , "<" et dispose d'un mode d'édition HTML.

Jean-Luc semblait assez satisfait de la combinaison Word + WLW + Blogger pour la production d'un blog.
A tester donc...

ScribeFire
Mentionné pour mémoire, ScribeFire est un plug-in FireFox permettant de facilité la publication de billet (y compris sur Blogger)

jeudi 17 décembre 2009

Quelques caratéristiques avancées du langage Python

Le langage Python dispose de quelques fonctions ou classes avancées qui peuvent se montrer vraiment utile dans des domaines pointus et pour le développement d'applications d'envergure.
Cet article reprend également quelques fonctions relatives aux éléments itérables.



__import__
__import__ est une primitive du langage permettant de charger un module (fichier .py) à la volée.
Cette primitive est principalement utilisé dans le cadre d'activation de modules plug-ins.

compile
La fonction compile permet de compiler du code source Pyhton à la volée.
Ce code source pouvant être contenu dans une chaine de caractère.
Par la suite, ce code compilé pourra être exécuté à l'aide de la primitive exec() ou eval().
Les cas d'utilisation de compile sont rare. Cependant, cette fonctionnalité convient très bien aux logiciels évolutifs (capable de se modifier par eux-mêmes).
Note:
Lors de l'utilisation de la fonction compile, il faut indique le mode de compilation.
Pour la compilation d'une expression à évaluer; le mode 'eval' convient très bien.
Bien qu'il soit indiqué que le mode 'single' convienne pour plusieurs instructions,  je n'ai eu un résultat probant  qu'en utilisant le mode 'exec' (voir l'exemple relatif à eval).

eval
Permet d'effectuer une évaluation (ou exécution) de code Python à la volée.
La fonction eval permet également d'utiliser le contexte actuel d'exécution (les variables définie et accessible au code appelant le seront également pour le code évalué) ou de définir le contexte d'exécution.
>>> myInt = 123
>>> sToEvaluate = 'myInt * 2'
>>> eval( sToEvaluate )
246
>>> # Modify the execution context
>>> eval( sToEvaluate, {'myInt' : 12} )
24
 
>>> sToEvaluate = 'x = 3\nprint( "%s * %s = %s" % (x, myInt, x*myInt) )'
>>> # Use 'exec' compilation mode because there are several lines!
>>> _compiled = compile( sToEvaluate, '/dev/null', 'exec' )
>>> eval( _compiled, {'myInt':12} )
3 * 12 = 36

execfile
Comme son nom le suggère, execfile permet d'exécuter le code contenu dans un fichier Python.
Cette exécution peut exploiter le contexte actuel ou redéfinir un contexte.

enumerate
Itération. Permet d'associer un indice/index numérique à chaque élément du contenu itérable.
>>> myList = [ 1, 13, 22, 4, 6, 17, 88, 65 ]
>>> for index, valeur in enumerate( myList ):
 print( 'valeur à l''index %s : %s' % (index, valeur) )

 
valeur à lindex 0 : 1
valeur à lindex 1 : 13
valeur à lindex 2 : 22
valeur à lindex 3 : 4
valeur à lindex 4 : 6
valeur à lindex 5 : 17
valeur à lindex 6 : 88
valeur à lindex 7 : 65
>>> myString = 'Hello !'
>>> for index, char in enumerate( myString ):
 print ( '%s - %s ' % (index, char) )

 
0 - H 
1 - e 
2 - l 
3 - l 
4 - o 
5 -   
6 - ! 

filter
Itération. La fonction filter permet de filtrer le contenu d'un tuple, list ou chaîne de caractères à l'aide d'une fonction. Seuls les éléments retournant True à la fonction de filtrage seront conservés.
None peut également être utilisé comme fonction de filtrage.
Dans ce cas, seul les éléments évalués comme True seront conservés (autrement dit, tout élément/objet ayant une référence). C'est une méthode bien pratique pour éliminer les éléments None d'une liste.
>>> myList = [ 1, 13, 22, 4, 6, 17, 88, 65 ]
>>> def myFilter( i ):
 return i>=10

>>> myFiltered = filter( myFilter, myList )
>>> for item in myFiltered:
 print item

 
13
22
17
88
65
>>> myString = "Voici une chaine de caractère. Je veux enlever les espaces"
>>> def isNotSpace( aChar ):
 return aChar != ' '

>>> myFilteredString = filter( isNotSpace, myString )
>>> print myFilteredString
Voiciunechainedecaractère.Jeveuxenleverlesespaces

reduce
Itération. Reduce permet d'appliquer une fonction de transformation de façon continu aux éléments d'une séquence.
Les 2 premiers éléments de la séquence sont utilisé avec la fonction.
Le résultat est alors utilisé avec le 3ième élément de la séquence pour rappeler la fonction.
Le nouveau résultat obtenu est alors utilisé avec le 4ième élément de la séquence pour rappeler la fonction.
Et ainsi de suite jusqu'à la fin de la séquence.

L'exemple suivant illustre parfaitement le fonctionnement de la fonction reduce.

>>> def somme( x, y):
...     print( '%d + %d = %d' % (x,y,x+y) )
...     return x+y
... 
>>> 
>>> reduce( somme, [1,2,3,4,5] )
1 + 2 = 3
3 + 3 = 6
6 + 4 = 10
10 + 5 = 15
15

Reduce peut également prendre une valeur initiale en troisième argument.
Cela permet de reprendre/compléter une opération reduce avec des nouvelles données.
>>> # poursuivre le calcul avec le dernier résultat obtenu (15) comme
>>> # valeur initiale
>>> reduce( somme, [6,7,8,9,10], 15 )
15 + 6 = 21
21 + 7 = 28
28 + 8 = 36
36 + 9 = 45
45 + 10 = 55
55
>>> # la somme des nombres de 1 à 10 fait 55

Cas d'utilisation:
  • traitement de flux de donnée.
  • Calcul récursif sur série de donnée (ex: calcul de moyenne).
map
Itération. Permet d'appliquer une fonction de transformation à chacun des éléments d'une structure itérable.
A noter que map peut être utilisé avec plusieurs listes. Dans ce cas, les éléments seront joints (un à un) et passés en paramètres de la fonction de transformation.
La fonction de transformation peut être None, dans ce cas, les éléments sont retournés (joint) tel quel et combinés en Tuple. Si l'une des liste est plus courte le tuple contiendra None pour l'élément manquant.
>>> myList = [1, 13, 22, 4, 6, 17, 88, 65]
>>> def mapTest( item ):
 return item*2

>>> newList = map( mapTest, myList )
>>> newList
[2, 26, 44, 8, 12, 34, 176, 130]

>>> myList = [1, 13, 22, 4, 6, 17, 88, 65]
>>> myList2 = ['A', 'b', 'c', 'd']
>>> def mapTest2( item1, item2 ):
        # str the items to avoids the error:
        # TypeError: cannot concatenate 'str' and 'NoneType' objects
 return str(item1)+'-'+str(item2)
>>> newList = map( mapTest2, myList, myList2 )
>>> newList
['1-A', '13-b', '22-c', '4-d', '6-None', '17-None', '88-None', '65-None']

>>> # mapping with None as function
>>> #   In this case this create TUPLES
>>> newList = map( None, myList, myList2 )
>>> newList
[(1, 'A'), (13, 'b'), (22, 'c'), (4, 'd'), (6, None), (17, None), (88, None), (65, None)]

>>> # mapping strings with None as function
>>> str1 = 'elements'
>>> str2 = 'langage'
>>> newList = map( None, str1, str2 )
>>> newList
[('e', 'l'), ('l', 'a'), ('e', 'n'), ('m', 'g'), ('e', 'a'), ('n', 'g'), ('t', 'e'), ('s', None)]
>>> # Autre usage des tuples
>>> for item1, item2 in map( None, str1, str2 ):
 print str( item1 )
 print ' +-> '+str( item2 ) 
e
 +-> l
l
 +-> a
e
 +-> n
m
 +-> g
e
 +-> a
n
 +-> g
t
 +-> e
s
 +-> None

all
Itération. Permet de tester l'homogénéité d'une séquence.
La fonction all retourne true si tous tous les éléments d'une séquence peuvent être évalués comme vrai.
Les éléments évalués peuvent être des références d'objets (assignés=True ou None=False) ou des expressions booléennes.
>>> mySeq1 = [ 1, 2, 'a', None ]
>>> mySeq2 = [ 1, 2, 'a', 3 ]
>>> mySeq3 = [ 1, 10, 20, 60 ]

>>> # Teste si tous les éléments sont assignés
>>> all( mySeq1 )
False
>>> all( mySeq2 )
True

>>> # Test d'homogénéité
>>> all( isinstance( el, int ) for el in mySeq1 )
False
>>> all( isinstance( el, int ) for el in mySeq2 )
False
>>> all( isinstance( el, int ) for el in mySeq3 )
True

>>> # Utilisation des [ ] pour construire un élément itérable
>>> print [ isinstance( el, int ) for el in mySeq3 ]
[True, True, True, True]
>>> # Test avec la nouvelle notation
>>> print all( [ isinstance( el, int ) for el in mySeq3 ] )
True

any
Itération. Permet de tester facilement le contenu d'une séquence.
La fonction any retourne true si au moins un des éléments de la séquence séquence peut être évalué comme vrai.
Les éléments évalués peuvent être des références d'objets (assignés ou None) ou des expressions booléennes.

>>> mySeq1 = [ 1, 2, 'a', None ]
>>> mySeq2 = [ 1, 2, 'a', 3 ]
>>> mySeq3 = [ 1, 10, 20, 60 ]

>>> print any( el > 10 for el in mySeq1 )
True
>>> print any( el > 10 for el in mySeq2 )
True
>>> print any( el > 10 for el in mySeq3 )
True
>>> print any( [ el > 10 for el in mySeq1 ] )
True
>>> print any( [ el > 10 for el in mySeq2 ] )
True
>>> print any( [ el > 10 for el in mySeq3 ] )
True
>>> print any( [ isinstance( el, str ) for el in mySeq1 ] )
True
>>> print any( [ isinstance( el, str ) for el in mySeq2 ] )
True
>>> print any( [ isinstance( el, str ) for el in mySeq3 ] )
False
Petit exemple complémentaire sur le blog amix.dk

lundi 14 décembre 2009

Sql Serveur - Converting une string Hexa en caratère

En Sql Serveur, il n'est pas si simple de convertir une string contenant une valeur hexadécimale en en caractère.
J'ai trouvé la réponse à cette épineuse question sur l'article "T-SQL Convert Hex String to Varbinary" de Peter DeBetta. La fonction HexStrToVarBin de Peter permet ce petit tour de passe-passe.

Exemple:
-- === CONVERT SIMPLE BYTE ================
DECLARE @strData Varchar(2)
-- Some programming extracting a hex digit
SET @strData = '4A'
print 'Conversion on simple char' 
print Char( dbo.HexStrToVarBin( '0x'+@StrData ) ) 

-- === CONVERT SEQUENCE OF BYTES ==========
DECLARE @strData2 Varchar(20)
-- Some programming extracting a hex digit
SET @strData2 = '4A4B4C4D'
print 'Conversion to sequence of char' 
print Cast( dbo.HexStrToVarBin( '0x'+@StrData2 ) as Varchar(10) )

Résultat:
Conversion on simple char
J
Conversion to sequence of char
JKLM

jeudi 10 décembre 2009

Python - Cas pratique 2

Deuxième cas pratique d'utilisation de script Python utilisant XML (libxml), XSL (d'un browser), les itérables et ODBC (pyODBC).

En résumé, ce script Python effectue les opérations suivantes:
  1. Connecter une DB MS SqlServeur pour lister le nom de certaines tables et leurs colonnes (en utilisant sysobjects et syscolumns).
  2. Etablir une structure Python en mémoire avec ces informations.
  3. Enrichir la structure (avec des informations complémentaires comme les datatypes, type delphi, etc)
  4. Transformation de la structure en document XML + enregistrement dans un fichier.
  5. Utilisation d'une XSL pour générer des classes Delphi sur base du contenu XML.
    Pour ce faire, ouvrir le fichier XML avec un Browser. Le code Delphi résultant est visible dans Browser. Utiliser de préférence FireFox car IE6 (par exemple) à la mauvaise manie d'ignorer les CR pour un output de type "text".
Code source, fichier xml et xsl disponibles ici.


Information relative au code source:
Le fichier XML imtermédiaire est fourni avec le code source. Cependant, son contenu a été fortement et volontairement altéré (name scrambling). D'autre part, il ne reste plus d'information sensible sur la ConnectionString dans le code Python.
La transformation XSL du type TzzzDateTime ne sera pas correct car, bien évidemment, il ne s'agit pas du type réel. Bien que le principe de fonctionnement soit correct, le code généré avec cet exemple ne compilera pas.

mardi 8 décembre 2009

Nouvelles technologies Google

Voici quelques vidéos relatives aux nouvelles technologies Google (à voir absolument).
Dans les deux premières vidéos ci-dessous, Google exploite l'avantage que peut offrir un appareil photographique embarqué sur les appareils mobiles (GSM).

Recherche sur base d'une image
Après la recherche vocale et sur base de texte écrit, voici la recherche sur base d'une image.
Dans la vidéo ci-jointe, Google va encore plus loin en permettant d'obtenir rapidement des informations à propos d'un objet d'art, d'un livre, d'un code barre, d'un monument connu ou même encore d'un texte photographié.



Google Favorite place
Encore un usage pratique d'une capture d'image (barre code 2D) depuis un appareil mobile.



Résultat en temps réel
Permet de suivre l'actualisation en temps réel d'une recherche sur un sujet précis.



Source: voir cet article de Clubic

Sql Database Space (sp_SDS)

Introduction
Parmi les tâches journalières d'un DBA, il y a la surveillance journalière de la taille des DB.
La capture et le stockage de cette information permettant par ailleurs d'anticiper les futurs besoins et problèmes de stockage.


Cas pratique
Pour les développeurs impliqués dans des processus de migration de données,  la surveillance de la taille des DBs (espace libre par fichier physique) peut également s'avérer utile. Il servira, par exemple, à placer judicieusement des opérations SHRINKFILE pour éviter l'engorgement du/des disque(s) en cours de test migration.
En exemple pratique, la migration d'une table de 50 millions de records vers de nouvelles structures peut générer d'assez grosses variations en besoin de stockage. Même avec un recovery model=simple pour limiter le gonflement du transaction log, un delta de plusieurs gigas (15Go dans mon cas) entre deux filegroups est tout à fait possible.

La stored procedure sp_SDS
La stored procedure sp_SDS de Richard Ding est, encore une fois, une des nombreuses perles qu'internet dissimule si facilement.
Compatible Sql Serveur 2000, 2005 et 2008, sp_SDS (Sql Database Space) permet de connaitre exactement l'espace physique occupé par une DB (ou  toutes les DB d'un serveur).
Outre la consommation physique, sp_SDS indique également le ratio d'occupation de la DB (son taux de remplissage). Cette dernière information peut s'avérer extrêmement pertinente durant un processus de migration de données.
Cerise sur le gâteau, sp_SDS peut fournir ses statistiques par fichier composant la (ou les) DB(s).

Pour plus d'information:

lundi 7 décembre 2009

Décorateur en Python (solution au typage non stricte)

Introduction - Le typage non strict
Bien que Python supporte très bien le modèle orienté objet, le typage des paramètres (dans l'appel des fonctions) n'est pas du type "strict".

Il est donc possible de passer une chaîne de caractère ou un objet quelconque à une fonction/méthode là ou le développeur a prévu un entier.

Bien de prônant l'utilisation et le renforcement du typage (stricte), je dois reconnaître que l'approche du  typage "non stricte" des paramètres et variable en Python permet une plus grande souplesse dans les développements.
C'est quelque-chose qu'il faut tester dans ce langage et ses structures pour en entre-apercevoir la portée. Cette souplesse est certainement l'un des éléments à l'origine de la popularité de Python. Même l'environement .Net s'y est mis en introduisant le mot clé "var".

Cependant, malgré cet avantage le typage non strict des paramètres reste quand même problématique car il prône l'erreur.

Solutions
Il existe plusieurs solutions pour éviter ces problèmes de typage.
  1. D'une façon générale, et bien que cela ne soit pas suffisant, une documentation préçise permet déjà au développeur consencieux d'utiliser correctement la fonction.
    Au moins, ce dernier n'aura pas à déviner/décoder le type à passer à la fonction.
  2. Utiliser un notation (préfix) identifiant le type de paramètre.
    Bien qu'également inssufisant, cette pratique est largement répandue chez les bons développeurs.
    C'est ausci que l'on retrouve un s pour string, ex: sVilleName; i pour integer, ex: iPostalCode; f pour float, eg: fSalary; ii pour interface, eg: iiPostalCodeXmlNode; et ainsi de suite. 
  3. Vérification systématique des  paramètres en tête de fonction (à l'aide de la fonction isinstance qui permet de tester le type du paramètre). 
  4. Finalement, il reste l'utilisation des décorateurs (voir ci-dessous).

Qu'est-ce qu'un décorateur
Un décorateur, est une fonction particulière qui permet de "décorer" une autre fonction. Pour ce faire, Python permet de déclarer une fonction décoratrice et met en place une syntaxe permettant de décorer les fonctions et méthodes avec un ou plusieurs décorateurs (fonctions décoratrices).

Un décorateur et son principe peut être grossièrement décrit avec la métaphore suivante.
C'est un peu comme les décorations de Noel sur un sapin. Pour atteindre les branches d'un sapin décoré, il faut préalablement débrancher les guirlances électriques, passer le barrage des décorations et éviter de casser les boules de verres.
En programmation c'est un peu pareil, pour atteindre la fonction à éxécuter (et l'exécutée), il faudra passer le barrage du décorateur (la fonction décoratrice).
La fonction décoratrive sera exécutée par Pyhton avant l'appel de la fonction décorée.

Vérification stricte des paramètres avec un décorateur
L'un des usages communs des décorateurs est la vérification des paramètres.
dans ce cadre d'utilisation, la fonction décoratrice vérifiera les conditions d'exécution (ou transformera les paramètres). En cas d'erreur, la fonction décoratrice remplira son rôle en retournant une erreur (la boule de verre qui casse :-) )

Exemple de mise en place de décorateur
Comprendre le fonctionnement interne d'un décorateur demande un peu de gymnastique intellectuel.
Vous trouverez plus bas quelques notes pouvant éventuellement facilite cette compréhension.

# Définition du décorateur
#
def only_int(func):
    """decorator pour vérifier que l'argument
est seulement un entier"""
    def _only_int( arg ):
        if not isinstance( arg, int ):
            raise TypeError("'%s' doit etre un entier" % str(arg))
        return func(arg)
    return _only_int

# definition de la function absmod3 
#   décorée avec la fonction only_int
@only_int
def absmod3(a):
    return abs(a)%3

absmod3( 100 )
# resultat: 1


absmod3( 12.45 )
# produit l'erreur suivante
#
Traceback (most recent call last):
  File "", line 1, in 
    absmod3( 12.45 )
  File "", line 6, in _only_int
    raise TypeError("'%s' doit etre un entier" % str(arg))
TypeError: '12.45' doit etre un entier 


Notez que la fonction de décoration only_int reçoit en référence la fonction a décorer (func).  La fonction à décorée (func) peut être appelée avec son unique argument (ici func(arg) ).

La fonction de décoration only_int retourne une référence vers la fonction de vérification _only_int (_only_int étant une nested/inner function du décorateur).

Pour résumer, le décorateur only_int est une fonction qui met en place une fonction d'interception sur un argument qui dans le cas des décorateur est une référence de fonction.
C'est dans cette technique que s'exprime toute la puissance du typage non-strict dy Python.


Au démarrage du programme (parsing du code source), Python place donc les fonctions d'interception (décorateurs) sur les appels des fonctions décorées.


En gros, en appelant la fonction absmod3 décorée avec only_int:
La fonction only_int (paramètre func=absmod3) installera la fonction de vérification _only_int comme intermédiare aux appels de absmod3. Au run-time, l'interpréteur appelera _only_int qui lui même rapellera la function référencée par func (donc absmod3). Au passage, _only_int vérifiera le paramètre qui sera passé à absmod3.

dimanche 6 décembre 2009

Quelques How-to pour Python (Regular Expressions, tri, socket)

Petite référence vers l'article "Regular Expression HOWTO" décrivant l'utilisation des régulars expressions sous Python.
Depuis la page "How To" générale, il est également possible d'accéder aux articles suivants:
  • Curses Programming with Python: An introduction to writing text-mode programs using Python and curses.
  • Regular Expression HOWTO: An introduction to using regular expressions and the re module to process text.
  • Socket Programming HOWTO, by Gordon McMillan: Sockets are used nearly everywhere, but are often misunderstood. This is a high-level overview of sockets, that doesn't cover all the fine points (there are a lot of them), but will give you enough background to begin using them decently.
  • Sorting Mini-HOWTO, by Andrew Dalke: A little tutorial showing a half dozen ways to sort a list with the built-in sort() method.
  • Unicode HOWTO: An introduction to Python's features for processing Unicode data.
Quelques références qui valent la peine d'une petite note.

    Structures en Python

    Ce qui est un peu déstabilisant avec Python c'est qu'une partie des structures/stéréotypes que l'on identifieraient facilement en C ou Pascal portent des noms/stéréotypes différents en Python.
    C'est ainsi qu'une "séquence" Python ressemble a s'y méprendre à un array en Pascal. C'est un peu déstabilisant lorsque l'on vient d'un autre langage.
    Je vais donc passer un peu de temps à synthétiser ces structures en vue d'en faire un petit article.

    3 décembre 2009:
    Premier jet de l'article enfin terminé.  Comme pour beaucoup d'autres articles, il faut pouvoir trouver le temps nécessaire à la rédaction...
    Si vous êtes déjà quelque peu familiarisé avec Python, jetez néanmoins un coup d'oeil sur la dernière section intitulée "Utilisation des mappings et optimisation". 

    6 décembre 2009:

    La presque totalité des exemples sont disponibles dans le fichier structures.py.

    Les séquences
    L'une des notions les plus importantes en Python est bien celle des séquences.
    Les séquences sont des objets "container"... donc destinés à contenir/maintenir des données ou objets.
    Le terme "séquence" est utilisé parce les objets de ce type sont "Itérable" (comprendre que la séquence peut être parcourue de façon "naturelle").
    Le contenu de ces objets peut donc être facilement et systématiquement balayé.
    Python dispose de plusieurs structures de données séquentielles (tuple, list, bytearray, str, unicode).

    Certaines séquences sont dites immuables et les éléments individuels ne peuvent pas être modifiés, c'est le cas des chaines de caractères (str appelé string, unicode), les tuples et les type bytes.
    Seul une opération de ré-affectation peut permettre la manipulation du contenu (phénomène identique au type string de la plateforme .Net).

    D'un autre coté, les structures séquentielles tel que les lists et les array (bytearray) sont des séquences modifiable. En d'autre terme, il est possible de modifier les éléments la composant de façon individuel sans passer par des opérations de ré-affectation.

    La programmation d'éléments/objets séquentiable (et donc itérable) est facile et autorise l'utilisation de syntaxe simple mais néanmoins très puissante.
    Un exemple typique de la puissance de l'itération est l'instruction for en Python (équivalent du "foreach" en C#).

    Les Tuples

    Les tuples sont des séquences non modifiables (immuable pour reprendre le terme 
    La notation des tuples ressemble fort aux types énumérés du Pascal sans pour autant avoir les même similitudes.
    Les tuples permettent de maintenir une séquence de valeurs (de tous types, y compris des tuples eux-mêmes).
    Cette structure immuable (dont on ne peut pas modifier indépendamment les valeurs) est principalement utilisée pour maintenir une séquence de "valeurs constantes".
    Les tuples se montrent également fort utiles lorsqu'une fonction/méthode doit renvoyer des multiples valeurs. En pascal, on utiliserait un "record", dans ce cas préçis, pour retourner ces valeurs multiples. Python à l'avantage de permettre la définition/création du tuple (et types inclus) à la volée, ce qui offre une grande souplesse.
    Il est possible d'accéder les différents éléments d'un tuple par l'intermédaire d'un index (0 to len-1) ou en utilisant les principes d'itération.
    Bien qu'un peu déstabilisants dans un premier temps, en autre parce que le tuple semble faire double emploi avec les listes, on constate rapidement que les tuples sont des éléments de programmation efficace et puissant.

    Finalement, bien qu'il ne soit pas possible de modifier indépendamment les éléments d'un tuple, il est possible de recomposer un nouveau tuple à partir d'un ancien tuple en effectuant une opération de ré-affectation:
    monTuple = monTuple + ( AjouterCetteValeur )
    Notez que la nouvelle valeur à inclure se trouve entre parenthèse (la notation d'un tuple).
    En utilisant cette notation, on constate que le nouveau tuple n'est autre que le résultat de la concaténation de deux autres tuples.

    # Utilisation de Tuple
    def testTuple():
     print( '--- Tuple testing ------------------------' )
     # Déclaration avec valeur multiple
     myTuple = ( 'Une valeur', 128, None, 4+3 )
     # énumeration
     print( 'Enumeration Tuple' )
     for i in myTuple:
      print( '  %s' % i )
     # Test d'inclusion
     if 128 in myTuple:
      print( 'Contient la valeur 128' )
     # Un tuple est 'non modifiable'.
     # L'ajout de valeur passe par une operation d'assignation
     myTuple = myTuple + ('Un autre tuple', 4)
     
     # Déclaration d'un tuple vide
     anotherTuple = ()
     anotherTuple = anotherTuple + ( 1, 5, 7, 'a' )
     anotherTuple = anotherTuple + myTuple
    
     # L'execution de la ligne suivant produit une erreur
     try:
      anotherTuple = anotherTuple + 998 # Operation interdite
     except TypeError, err:
      print( 'Ajout de la valeur 998 au tuple produit '+
       'l\'erreur suivante' )  
      print( '  %s' % err ) # cette ligne sera executée
      
     # L'execution de la ligne suivant produit une erreur
     try:
      anotherTuple[0] = 'Modifier la valeur'
     except TypeError, err:
      print( 'Modifier directement la valeur d\'un element '+
             'd\un tuple produit l\erreur suivante' )
      print( '  %s' % err ) 
    
     # longueur et accès a un simple élément
     if len( anotherTuple )>0:
      print( 'longueur de anotherTuple= %s' % len(anotherTuple) )
      print( 'Deuxième valeur de anotherTuple= %s' % anotherTuple[1] )
    
     # Compostion d'un tuple complexe
     complexTuple = ( ['1','5','10'], anotherTuple, ('a','1'),
       ('b',('un','deux')) )
     print( 'contenu de complexTuple' )
     print( complexTuple )
     print( 'longueur de complexTuple= %s' % len( complexTuple ) )
     for i in range( len(complexTuple) ):
      print( '  élément %s est %s' % (i, complexTuple[i]) )
    

    Liens utiles:
    Les listes (séquence)
    Les listes sont des éléments de type "séquence".

    Python utilise une notation à l'aide de [ ] pour créer et manipuler les listes. L'instruction list() permet également de créer formellement une liste.
    Bien que la notation et l'utilisation des liste soit similaire aux arrays du Pascal, il sont à ne  pas a confondre avec les arrays du Pascal!

    Les éléments d'une liste sont néanmoins accessibles en utilisant un index (compris entre 0 et len-1).
    Il est également possible d'extraire une séquence (sous liste) d'une liste donné.

    Voici quelques exemples de manipulation de liste.

    # Crée une liste vide
    myEmptyList = []
    
    # définition directe 
    myList = [ '1', 123, 'Hello' ]
    
    # définition à l'aide de list() et initialisation à l'aide d'une autre liste
    myList4 = list( [1-2,'World','is','$T0n€'] )
    # donnera le résultat [-1, 'World', 'is', '$T0n\x80']
    
    # définition à l'aide de list() et initialization à l'aide d'un Tuple
    myList3 = list( (1,'helloZ','test') )
    # donnera le résultat [1, 'helloZ', 'test']
    
    # définition à l'aide des méthodes de list()
    myList5 = list()
    myList5.append( 123 )
    myList5.append( 'Zut' )
    myList5.append( 'Il y a du monde' )
    myList5
    # donnera [123, 'Zut', 'Il y a du monde']
    
    # Extraction d'élément par index 
    print( myList5[0] )
    # L'utilisation d'un index négatif permet d'accéder
    #   aux éléments depuis la fin de la liste (Python
    #   ajoute la longueur de la liste à l'index)
    print( myList5[-1] )
    # produira 'Il y a du monde'
    
    # Extraction d'une séquence
    myList6 = myList4[1:3]
    myList6 
    # produira le résultat ['World', 'is', '$T0n\x80']
    

    Les listes (tout comme les Tuples) sont des éléments itérables.
    Il est donc possible décrire un code comme le suivant:
    for item in myList5:
         print( item )
    
    ce qui produira le résultat:
    123
    Zut
    Il y a du monde

    Liens utiles:

    Les arrays (séquence)
    Le type bytearray est une structure de type séquence destiné a contenir des bytes.
    Le type bytes (équivalent de str en Pyhton 2.6) sont immuables.
    Les bytearray lui n'est pas immuable et peut donc être modifié. Ce type dispose d'ailleurs d'une grande partie des fonctions dévolues au type str.

    Comparé aux autres types de séquences, les arrays (bytearrays) ne présente pas, de prime abord, d'intérêt particulier comme cela pourrait être le cas dans des langages comme C et Pascal.
    En Pyhton, les types les plus intéressants restent quand même les list, tuples et mapping (ci-après)


    Les types bytes et bytearray présente principalement un intérêt lors d'interfacage avec des API, des OS ou du materiel. Ces types permettent de transposer les notions et les structures de type "string" d'autres langages directement en Python. Le type bytearray est donc utilisé pour facilité l'interopérabilité.
    A titre d'exemple, voici quelques références relatives a win32com, ironPython, Socket programming, etc.


    Il est par contre important de noter une finesse de convertion entre les types str, bytes, bytearray à partir de Python 3.0. En effet, à partir de cette version, le type str sera unicode.
    Plus d'information sont disponible le document "What's new in Python 2.6" à la section "Byte Literals" sur docs.python.org.

    Les mappings (dictionnaire)
    Bien qu'également itérable, les mappings ne sont pas des séquences à proprement parler.
    Le mapping est un dictionnaire permettant d'associer et maintenir des pairs objet + clé (généralement des mots clés/chaines de caractères).
    Les différents objets sont accessible via leur clé. Pour maintenir de bonne performance, les mappings utilisent des tables de hashing pour optimiser les temps d'accès/recherches.

    L'exemple ci-dessous démontre l'usage général des mappings.
    >>> # Definition d'un dictionnaire (directement)
    >>> parents = { 'man' : 'dodo', 'woman' : 'fanfan' }
    >>> parents
    {'woman': 'fanfan', 'man': 'dodo'}
    >>> for key, value in parents.items():
     print 'the %s is %s' % (key, value)
    
     
    the woman is fanfan
    the man is dodo
    
    >>> # Definition d'un dictionnaire via l'objet dict()
    >>> #   nb: il est également possible d'initialiser le 
    >>> #   dictionnaire dans le constructeur
    >>> kids = dict()
    >>> kids['kid 1'] = 'didi'
    >>> kids['kid 2'] = 'jess'
    >>> kids['kid 3'] = 'ben'
    >>> kids['kid 4'] = 'lili'
    >>> kids
    {'kid 1': 'didi', 'kid 3': 'ben', 'kid 2': 'jess', 'kid 4': 'lili'}
    >>> familly = dict( parents )
    >>> familly.update( kids )
    >>> familly
    {'woman': 'fanfan', 'kid 1': 'didi', 'kid 3': 'ben', 'kid 2': 'jess', 'kid 4': 'lili', 'man': 'dodo'}
    >>> familly.get( 'kid2', 'NOT AVAILABLE' )
    'NOT AVAILABLE'
    >>> familly.get( 'kid 2', 'NOT AVAILABLE' )
    'jess'
    >>> familly.has_key( 'glops' )
    False
    >>> familly.has_key( 'man' )
    True
    >>> familly.iterkeys()
    <dictionary-keyiterator object at 0x0119A2A0>
    >>> for item in familly.iterkeys():
     print item
    
     
    woman
    kid 1
    kid 3
    kid 2
    kid 4
    man
    >>> for iterKey, iterValue in familly.iteritems():
      print '%s -> %s' % (iterKey, iterValue)
    
      
    woman -> fanfan
    kid 1 -> didi
    kid 3 -> ben
    kid 2 -> jess
    kid 4 -> lili
    man -> dodo
    

    Liens utiles
    Utilisation des mappings et optimisation

    Il faut cependant prendre note d'une notion importante relative à l'optimisation des programmes.
    Il est par exemple possible d'obtenir les éléments d'un dictionnaire à l'aide de myDico.items() ou myDico.IterItems(). Un principe similaire s'applique également pour l'obtention séparée des clés ou valeurs.

    La fonction items() crée une copie des éléments composant le mapping.
    Cela consomme inévitablement de la mémoire. Il faudra l'utiliser avec attention pour évitant de l'appliquer sur des mappings de grande taille. Par ailleurs, le fait même de copier le contenu requière que la copie soit entièrement effectuée avant le retour d'appel de la fonction items().
    Finalement, puisqu'il s'agit d'une copie, les modifications faites sur la copie ne sont pas remportées dans le dictionnaire source.

    La fonction iteritems() crée un itérateur sur les éléments composant le mapping.
    Le retour de fonction iteritem() est immédiat car il n'y a pas d'opération de copie.
    Le contenu du dictionnaire ne doit même pas être dénombré/visité pour retourner un itérateur.
    Cela permet d'économiser à la fois de la mémoire et les ressources CPU.
    La charge CPU d'iteration sera répartie entre les différents appels (à l'itérateur) qui sera utilisé pour balayer le dictionnaire.
    A noter que dans ce cas, les éléments du dictionnaire disponible dans l'itérateur ne sont également pas modifiable via les variables d'itération.
    En effet, les variables d'itération contiennent une copie de la valeur/clé du dictionnaire et non une référence (ce qui les rendraient modifiable).

    >>> familly.items()
    [('woman', 'fanfan'), ('kid 1', 'didi'), ('kid 3', 'ben'), ('kid 2', 'jess'), ('kid 4', 'lili'), ('man', 'dodo')]
    
    >>> # Utilisation d'un itérateur
    >>> familly.iteritems()
    <dictionary-itemiterator object at 0x0119A3C0>
    >>> for iterKey, iterValue in familly.iteritems():
     if iterKey=='man':
      iterValue = 'Man will not be replaced (hehe!)'
      print 'replace attempt'
    
    replace attempt
    >>> familly.items()
    [('woman', 'fanfan'), ('kid 1', 'didi'), ('kid 3', 'ben'), ('kid 2', 'jess'), ('kid 4', 'lili'), ('man', 'dodo')]
    
    >>> # Utilisation d'une copie
    >>> for iterKey, iterValue in familly.items():
     if iterKey=='man':
      iterValue = 'Man will not be replaced (neither!)'
      print 'Replace attempt 2'
      
    Replace attempt 2
    >>> familly.items()
    [('woman', 'fanfan'), ('kid 1', 'didi'), ('kid 3', 'ben'), ('kid 2', 'jess'), ('kid 4', 'lili'), ('man', 'dodo')]
    

    jeudi 3 décembre 2009

    Review d'éditeur UML

    Sur le judebert.com, un développeur Java évalue des éditeurs UML open source afin de trouver le meilleur selon "ses critères" pour l'aider dans ses développements.

    Voici donc une liste de ses articles (et résumé de l'évaluation):

    mercredi 2 décembre 2009

    Open ModelSphere l'outil open source de modélisation UML, DB et Processus

    Open ModelSphere est un logiciel de modélisation UML complet mais également de donnée et de processus.
    Ecrit en Java, ce logiciel est "cross-platform" et disponible gratuitement.

    Diagramme UML
    Open ModelSphere permet de créer les diagrammes UML suivants:
    • Activité
    • Classe
    • Collaboration
    • Composant
    • Deploiement
    • Package
    • Sequence
    • Etat-transition
    • Use-case
    Diagramme de donnée
    Open ModelSphere gère à la fois les modèles conceptuels et logiques de données.
    Les modèles conceptuels peuvent être convertis en modèles relationnelles (et vice versa).
    Comme tous les logiciels de bonne facture, open ModelSphere permet de générer un diagramme directement depuis une DB relationnelle existante. Bien entendu, il est également possible de reporter les modifications effectuées avec le logiciel dans la DB.
    Le logiciel dispose également de module expert permettant de valider l'intégrité et la cohérence de l'architecture des DB (fonctionnalité par ailleurs récemment apparu dans Sql Serveur 2008).
    Finallement, open ModelSphere peut se supporte de nombreux type de DB. Certaines DB comme DB2, Informix ou Oracle dispose de plug-ins pour inclure leurs concepts spécifiques. 

    Autres diagrammes
    Open ModelSphere permet également de modéliser les flots de données, les processus, les workflows (et logiques) de l'entreprise. Il est possible de spécifier des ressources, transactions, échanges de communication, des coûts, etc. Les processus peuvent êtres divisés en sous processus.

    Plus d'informations:
    Une review de Open ModelSphere par judert.com
    En Juillet 2009, judert.com testait open ModelSphere (lien ici) pour trouver un bon éditeur UML pour ses développements Java.
    Le verdit est assez tranché, à savoir:
    " Ce n'est pas intuitif, and je ne sais pas obtenir de support, comment est-il possibe de l'utiliser? Open ModelSphere ne rempli pas mes exigences pour un outil de modélisation UML utile. "
    Cependant, il faut quelque peu nuancer ces propos.
    En effet, open ModelSphere est plus qu'un simple éditeur UML, il couvre d'autres domaines dans lesquels il pourrait offrir une bonne évaluation. Je pense, par exemple, aux diagrammes de données.

    mardi 1 décembre 2009

    sp_force_shrink_log pour Sql Serveur 2005

    Déja relaté dans l'article "Utilitaires pour SQL Server 7", la stored procedure sp_force_shrink_log  de Andrew Zanevsky (AZ databases) permet de tronquer le transaction log de façon nettement plus efficace que DBCC SHRINKFILE.
    En effet, DBCC SHRINKFILE ne fait que tronquer la partie inactive en fin du fichier de transaction.
    Si le transaction log fait 25Go et que "le pointeur de transaction" se trouve actuellement en fin de fichier (ex: à l'offset 24.7 Go, ce qui est assez fréquent); dans ce cas, DBCC SHRINKFILE ne libèrera que 300Mo.
    Il faudra attendre un roll-over vers le début du fichier de transaction pour avoir un résultat probant.

    Lorsque que le transaction log fait 25 Go et qu'il étouffe littéralement le système de fichier, il n'est pas forcement possible d'attendre patiemment l'opération de Roll-over sur le transaction log. Libérer de l'espace disque peut être impératif pour la bonne conduite d'autres opérations sql.
    C'est là qu'intervient précisément sp_force_shrink_log. Cette store procédure va déplacer le pointeur de transaction dans le transaction log en simulant des activités sql sur une table temporaire. Une fois le roll-over effectué (ce qui arrivera tôt ou tard), le transaction log sera efficacement tronqué et la stored procedure pourra interrompre son traitement.
    Malheureusement, la store procedure d'origine prévue pour SQL 7 ne fonctionne pas correctement sur SQL Serveur 2005. En effet, le résultat retourné par DBCC LogInfo (utilisé par sp_force_Shrink_log) a été modifié depuis SQL 7.
    Voici donc une petite mise à jour (faite par mes soins) du script sp_force_shrink_log pour SQL Serveur 2005 (sp_force_shrink_log_2005.sql).

    use master
    go
    if object_id( 'sp_force_shrink_log' ) is not null drop proc sp_force_shrink_log
    go
    create proc sp_force_shrink_log 
    /*
    *************************************************************
    Name: sp_force_shrink_log
    Description:
      Shrink transaction log of the current database in SQL Server 2005.
      Switch context to proper db to execute.
    Usage: exec sp_force_shrink_log <target_percent>, <target MB>, <iterations>, <backup options>
       exec pubs..sp_force_shrink_log
    
    Author:  Andrew Zanevsky, AZ Databases, Inc., 12/25/1999, v5 - 08/18/2000
       zanevsky@azdatabases.com
    
    Note:  D Meurisse, *no_company_info* , 12/01/2009, v5.1 - 12/01/2009
       Updated this wonderfull script to make it running on SQL 2005. 
       Updated the description and change version to v5.1 
       
    Input Params:
    -------------
    @target_percent  tinyint. default = 0. Target percentage of remaining shrinkable
          space. Defaults to max possible.
    @target_size_MB  int. default = 10. Target size of final log in MB.
    @max_iterations  int. default = 1000. Number of loops (max) to run proc through.
    @backup_log_opt  nvarchar(1000). default = 'with truncate_only'. Backup options.
    
    Output Params:
    --------------
    
    Return:
    
    Results:
    ---------
    
    Locals:
    --------
    @err  Holds error value
    
    Modifications:
    --------------
      V5.1 - Meurisse D. - 1 dec 2009 - small changes to make it working with Sql 2005
    
    ************************************************************* */
        @target_percent tinyint = 0,
        @target_size_MB int = 10,
        @max_iterations int = 1000,
        @backup_log_opt nvarchar(1000) = 'with truncate_only'
    as 
    
    set nocount on
    
    declare @db         sysname, 
            @last_row   int,
            @log_size   decimal(15,2),
            @unused1    decimal(15,2),
            @unused     decimal(15,2),
            @shrinkable decimal(15,2),
            @iteration  int,
     @file_max   int,
     @file     int,
     @fileid     varchar(5)
    
    select  @db = db_name(),
            @iteration = 0
    /* drop table #LogInfo
       drop table #LogFiles
    */
    create table #loginfo ( 
        id          int identity, 
        FileId      int, 
        FileSize    numeric(22,0), 
        StartOffset numeric(22,0), 
        FSeqNo      int, 
        Status      int, 
        Parity      smallint, 
        /* CreateTime  datetime */
     CreateLSN varchar(50)
    )
    
    create unique clustered index loginfo_FSeqNo on #loginfo ( FSeqNo, StartOffset )
    
    create table #logfiles ( id int identity(1,1), fileid varchar(5) not null )
    
    insert #logfiles ( fileid ) select convert( varchar, fileid ) from sysfiles where status & 0x40 = 0x40        
    select @file_max = @@rowcount
    if object_id( 'table_to_force_shrink_log' ) is null
     exec( 'create table table_to_force_shrink_log ( x nchar(3000) not null )' )
    insert  #loginfo ( FileId, FileSize, StartOffset, FSeqNo, Status, Parity, /*CreateTime*/ CreateLSN ) exec ( 'dbcc loginfo' )
    select  @last_row = @@rowcount
    select  @log_size = sum( FileSize ) / 1048576.00,
            @unused = sum( case when Status = 0 then FileSize else 0 end ) / 1048576.00,
            @shrinkable = sum( case when id < @last_row - 1 and Status = 0 then FileSize else 0 end ) / 1048576.00
    from    #loginfo
    select  @unused1 = @unused -- save for later
    
    select  'iteration'          = @iteration,
            'log size, MB'       = @log_size,
            'unused log, MB'     = @unused,
            'shrinkable log, MB' = @shrinkable,
            'shrinkable %'       = convert( decimal(6,2), @shrinkable * 100 / @log_size )
    print 'xx.10'
    while @shrinkable * 100 / @log_size > @target_percent 
      and @shrinkable > @target_size_MB 
      and @iteration < @max_iterations begin
        select  @iteration = @iteration + 1 -- this is just a precaution
    
        exec( 'insert table_to_force_shrink_log select name from sysobjects
               delete table_to_force_shrink_log')
        select @file = 0
        while @file < @file_max begin
            select @file = @file + 1
            select @fileid = fileid from #logfiles where id = @file
            exec( 'dbcc shrinkfile( ' + @fileid + ' )' )
        end
    
        exec( 'backup log [' + @db + '] ' + @backup_log_opt )
    
        truncate table #loginfo 
        insert  #loginfo ( FileId, FileSize, StartOffset, FSeqNo, Status, Parity, /* CreateTime */ CreateLSN ) exec ( 'dbcc loginfo' )
        select  @last_row = @@rowcount
    
        select  @log_size = sum( FileSize ) / 1048576.00,
                @unused = sum( case when Status = 0 then FileSize else 0 end ) / 1048576.00,
         @shrinkable = sum( case when id < @last_row - 1 and Status = 0 then FileSize else 0 end ) / 1048576.00
        from    #loginfo
    
        select  'iteration'          = @iteration,
                'log size, MB'       = @log_size,
                'unused log, MB'     = @unused,
                'shrinkable log, MB' = @shrinkable,
                'shrinkable %'       = convert( decimal(6,2), @shrinkable * 100 / @log_size )
    end
    
    if @unused1 < @unused 
    select  'After ' + convert( varchar, @iteration ) + 
            ' iterations the unused portion of the log has grown from ' +
            convert( varchar, @unused1 ) + ' MB to ' +
            convert( varchar, @unused ) + ' MB.'
    union all
    select 'Since the remaining unused portion is larger than 10 MB,' where @unused > 10
    union all
    select 'you may try running this procedure again with a higher number of iterations.' where @unused > 10
    union all
    select 'Sometimes the log would not shrink to a size smaller than several Megabytes.' where @unused <= 10
    
    else
    select  'It took ' + convert( varchar, @iteration ) + 
            ' iterations to shrink the unused portion of the log from ' +
            convert( varchar, @unused1 ) + ' MB to ' +
            convert( varchar, @unused ) + ' MB'
    
    exec( 'drop table table_to_force_shrink_log' )
     go
    if object_id( 'sp_force_shrink_log') Is Null
     select 'sp_force_shrink_log Not Created'
    else
     select 'sp_force_shrink_log Created'
    go 
    

    ressources:

    vendredi 27 novembre 2009

    lxml: Python et Xml

    L'un des meilleurs package XML disponible pour Python est lxml (aussi connu comme ElementTree).
    Ce package n'est pas disponible par défaut lors de l'installation de Python, il convient donc de faire le nécessaire (voir détails plus loin).

    Tutorial
    ElementTree dispose d'un excellent tutorial de prise en main qu'il ne faut surtout pas rater sur "code speak".
    Code Speak publie également une documentation pdf complète.
    L'article "ElementTree Overview" propose un court survol de eTree mais incluant néanmoins le parsing, chargement et sauvegarde.
    Voir également "Python XML processing with lxml"

    Exemple
    Cette exemple montre la création et la sauvegarde d'un document XML.
    Plus d'exemples sont disponibles dans l'excellent tutorial dédié a lxml.etree sur "Code Speak".
    >>> from lxml import etree
    >>> _rootNode = etree.Element( 'root-node' )
    >>> etree.SubElement( _rootNode, 'first-child' )
    <Element first-child at 124b3c0>
    >>> etree.SubElement( _rootNode, 'second-child' )
    <Element second-child at 1241e10>
    
    >>> _attr = _rootNode.attrib
    >>> _attr['UnAttribut']='UneValeur'
    >>> print( etree.tostring( _rootNode, pretty_print=True ) )
    <root-node UnAttribut="UneValeur">
      <first-child/>
      <second-child/>
    </root-node>
    
    >>> # sauvegarde dans un fichier
    >>> _tree = etree.ElementTree( _rootNode )
    >>> _f = open( 'test.xml', 'w' )
    >>> with _f:
     _tree.write( _f )
    
     
    >>> # autre sauvegarde
    >>> _tree.write( 'test2.xml' )
    

    Installation de lxml
    ElementTree fait partie du package lxml qui peut être installé depuis Python Package Index (PyPI) à l'aide de easy_install (voir cet article) .
    Le package lxml se base sur libXml et libXslt qui ne sont habituellement pas installés dans l'environment Windows (voir la note "installing lxml" sur "code speak" pour quelques infos complémentaires).
    En temps normal, les package PyPI contiennent les dépendances binaires (DLL) nécessaires et ces dernieres sont automatiquement installées par easy_install.
    Il arrive cependant que les dépendances binaires ne soient pas encore disponibles dans la dernière version d'un package (ce qui peut être le cas pour des packages en cours de développement).
    Dans ce cas, easy install retourne inévitablement le message d'erreur "** make sure the development packages of libxml2 and libxslt are installed **".
    Dans ce cas, il convient d'identifier dans PyPI la dernière version stable du package et de passer ce numéro de version en paramètre à easy_install.
    Ce fut le cas lors de ma tentative d'installation lxml à l'aide de easy_install ("best match" pour la version 2.2.3).
    Le commande d'installation correcte était finallement:
    easy_install lxml==2.2.2

    Informations relatives au port de LibXml sous Windows
    Zlatkovic dispose d'un site spécifique concernant le portage de LibXml sous Windows.
    Ce site propose également un lien ftp (ftp.zlatkovic.com/libxml/) permettant de télécharger les DLLs libXml et consort (à installer manuellement dans le répertoire c:\windows\).
    Comme le précise Zlatkovoc sur son site, il existe un certains nombre de dépendance entre libXml et d'autres librairies comme zlib et vicon (voir image ci-dessous).
    image: www.zlatkovic.com/images/libxml-pkgdeps.png
    A noter finalement qu'en installant LibXml, LibXslt, IconV and ZLib quelques utilitaires sont également installés et disponible en ligne de commande (xsltproc,  xmlcatalog, xmllint, minigzip).

    jeudi 26 novembre 2009

    Liste des commandes DBCC

    Voici un article de "Novick software" qui explique comment obtenir une liste de toutes les commandes DBCC de Sql Server.
    Cette liste  inclus les commandes non documentées ainsi que la syntaxe.



    DBCC activecursors [(spid)]

    DBCC addextendedproc (function_name, dll_name)

    DBCC addinstance (objectname, instancename)

    DBCC adduserobject (name)

    DBCC auditevent (eventclass, eventsubclass, success, loginname
                                                , rolename, dbusername, loginid)

    DBCC autopilot (typeid, dbid, tabid, indid, pages [,flag])

    DBCC balancefactor (variance_percent)

    DBCC bufcount [(number_of_buffers)]

    DBCC buffer ( {'dbname' | dbid} [, objid [, number [, printopt={0|1|2} ]
                                      [, dirty | io | kept | rlock | ioerr | hashed ]]])
                                             
    DBCC bytes ( startaddress, length )

    DBCC cachestats

    DBCC callfulltext

    DBCC checkalloc [('database_name'[, NOINDEX | REPAIR])]
                               [WITH NO_INFOMSGS[, ALL_ERRORMSGS][, ESTIMATEONLY]]

    DBCC checkcatalog [('database_name')] [WITH NO_INFOMSGS]

    DBCC checkconstraints [( 'tab_name' | tab_id | 'constraint_name' | constraint_id )]
                                              [WITH ALL_CONSTRAINTS | ALL_ERRORMSGS]

    DBCC checkdb [('database_name'[, NOINDEX | REPAIR])]
                                      [WITH NO_INFOMSGS[, ALL_ERRORMSGS]
                                        [, PHYSICAL_ONLY][, ESTIMATEONLY][,DBCC TABLOCK]

    DBCC checkdbts (dbid, newTimestamp)]

    DBCC checkfilegroup [( [ {'filegroup_name' | filegroup_id} ]
                                     [, NOINDEX] )] [WITH NO_INFOMSGS
                          [, ALL_ERRORMSGS][, PHYSICAL_ONLY][, ESTIMATEONLY][, TABLOCK]]

    DBCC checkident ('table_name'[, { NORESEED | {RESEED [, new_reseed_value] } } ] )

    DBCC checkprimaryfile ( {'FileName'} [, opt={0|1|2|3} ])

    DBCC checktable ('table_name'[, {NOINDEX | index_id | REPAIR}])
                                          [WITH NO_INFOMSGS[, ALL_ERRORMSGS]
                                          [, PHYSICAL_ONLY][, ESTIMATEONLY][, TABLOCK]]

    DBCC cleantable ('database_name'|database_id, 'table_name'|table_id,[batch_size])

    DBCC cacheprofile [( {actionid} [, bucketid])

    DBCC clearspacecaches ('database_name'|database_id,
                                   'table_name'|table_id, 'index_name'|index_id)

    DBCC collectstats (on | off)

    DBCC concurrencyviolation (reset | display | startlog | stoplog)

    DBCC config

    DBCC cursorstats ([spid [,'clear']])

    DBCC dbinfo [('dbname')]

    DBCC dbrecover (dbname [, IgnoreErrors])

    DBCC dbreindex ('table_name' [, index_name [, fillfactor ]]) [WITH NO_INFOMSGS]

    DBCC dbreindexall (db_name/db_id, type_bitmap)

    DBCC dbrepair ('dbname', DROPDB [, NOINIT])

    DBCC dbtable [({'dbname' | dbid})]

    DBCC debugbreak

    DBCC deleteinstance (objectname, instancename)

    DBCC des [( {'dbname' | dbid} [, {'objname' | objid} ])]

    DBCC detachdb [( 'dbname' )]

    DBCC dropcleanbuffers

    DBCC dropextendedproc (function_name)

    DBCC dropuserobject ('object_name')

    DBCC dumptrigger ({'BREAK', {0 | 1}} | 'DISPLAY' | {'SET', exception_number}
                                                 | {'CLEAR', exception_number})

    DBCC errorlog

    DBCC extentinfo [({'database_name'| dbid | 0}
                     [,{'table_name' | table_id} [, {'index_name' | index_id | -1}]])]

    DBCC fileheader [( {'dbname' | dbid} [, fileid])

    DBCC fixallocation [({'ADD' | 'REMOVE'},
                               {'PAGE' | 'SINGLEPAGE' | 'EXTENT' | 'MIXEDEXTENT'}
                                       , filenum, pagenum [, objectid, indid])

    DBCC flush ('data' | 'log', dbid)

    DBCC flushprocindb (database)

    DBCC free dll_name (FREE)

    DBCC freeproccache

    dbcc freeze_io (db)

    dbcc getvalue (name)

    dbcc icecapquery ('dbname', stored_proc_name
                                  [, #_times_to_icecap  (-1 infinite, 0 turns off)])
         Use 'dbcc icecapquery (printlist)' to see list of SP's to profile.
         Use 'dbcc icecapquery (icecapall)' to profile all SP's.
           
    dbcc incrementinstance (objectname, countername, instancename, value)

    dbcc ind ( { 'dbname' | dbid }, { 'objname' | objid }, { indid | 0 | -1 | -2 } )

    DBCC indexdefrag ({dbid | dbname | 0}, {tableid | tablename}, {indid |indname})

    DBCC inputbuffer (spid)

    DBCC invalidate_textptr (textptr)

    DBCC invalidate_textptr_objid (objid)

    DBCC iotrace ( { 'dbname' | dbid | 0 | -1 }
                                 , { fileid | 0 }, bufsize, [ { numIOs | -1 }
                                   [, { timeout (sec) | -1 } [, printopt={ 0 | 1 }]]] )

    DBCC latch ( address [, 'owners'] [, 'stackdumps'])

    DBCC lock ([{'DUMPTABLE' | 'DUMPSTATS' | 'RESETSTATS' | 'HASH'}] |
                                              [{'STALLREPORTTHESHOLD', stallthreshold}])

    DBCC lockobjectschema ('object_name')

    DBCC log ([dbid[,{0|1|2|3|4}[,['lsn','[0x]x:y:z']|['numrecs',num]|['xdesid','x:y']
                     |['extent','x:y']|['pageid','x:y']|['objid',{x,'y'}]|['logrecs',
                               {'lop'|op}...]|['output',x,['filename','x']]...]]])

    DBCC loginfo [({'database_name' | dbid})]

    DBCC matview ({'PERSIST' | 'ENDPERSIST' | 'FREE' | 'USE' | 'ENDUSE'})

    DBCC memobjlist [(memory object)]

    DBCC memorymap

    DBCC memorystatus

    DBCC memospy

    DBCC memusage ([IDS | NAMES], [Number of rows to output])

    DBCC monitorevents ('sink' [, 'filter-expression'])

    DBCC newalloc - please use checkalloc instead

    DBCC no_textptr (table_id , max_inline)

    DBCC opentran [({'dbname'| dbid})] [WITH TABLERESULTS[,NO_INFOMSGS]]

    DBCC outputbuffer (spid)

    DBCC page ( {'dbname' | dbid}, filenum, pagenum
                                     [, printopt={0|1|2|3} ][, cache={0|1} ])

    DBCC perflog

    DBCC perfmon

    DBCC pglinkage (dbid, startfile, startpg, number, printopt={0|1|2}
                                                  , targetfile, targetpg, order={1|0})

    DBCC pintable (database_id, table_id)

    DBCC procbuf [({'dbname' | dbid}[, {'objname' | objid}
                                              [, nbufs[, printopt = { 0 | 1 } ]]] )]

    DBCC proccache

    DBCC prtipage (dbid, objid, indexid [, [{{level, 0}
                                        | {filenum, pagenum}}] [,printopt]])

    DBCC pss [(uid[, spid[, printopt = { 1 | 0 }]] )]

    DBCC readpage ({ dbid, 'dbname' }, fileid, pageid
                                     , formatstr [, printopt = { 0 | 1} ])

    DBCC rebuild_log (dbname [, filename])

    DBCC renamecolumn (object_name, old_name, new_name)

    DBCC resource

    DBCC row_lock (dbid, tableid, set) - Not Needed

    DBCC ruleoff ({ rulenum | rulestring } [, { rulenum | rulestring } ]+)

    DBCC ruleon (  rulenum | rulestring } [, { rulenum | rulestring } ]+)

    DBCC setcpuweight (weight)

    DBCC setinstance (objectname, countername, instancename, value)

    DBCC setioweight (weight)

    DBCC show_statistics ('table_name', 'target_name')

    DBCC showcontig (table_id | table_name [, index_id | index_name]
                             [WITH FAST, ALL_INDEXES, TABLERESULTS [,ALL_LEVELS]])

    DBCC showdbaffinity

    DBCC showfilestats [(file_num)]

    DBCC showoffrules

    DBCC showonrules

    DBCC showtableaffinity (table)

    DBCC showtext ('dbname', {textpointer | {fileid, pageid, slotid[,option]}})

    DBCC showweights

    DBCC shrinkdatabase ({dbid | 'dbname'}, [freespace_percentage
                                                [, {NOTRUNCATE | TRUNCATEONLY}]])

    DBCC shrinkfile ({fileid | 'filename'}, [compress_size
                                         [, {NOTRUNCATE | TRUNCATEONLY | EMPTYFILE}]])

    DBCC sqlmgrstats

    DBCC sqlperf (LOGSPACE)({IOSTATS | LRUSTATS | NETSTATS | RASTATS [, CLEAR]}
                                                      | {THREADS} | {LOGSPACE})

    DBCC stackdump [( {uid[, spid[, ecid]} | {threadId, 'THREADID'}] )]

    DBCC tab ( dbid, objid )

    DBCC tape_control {'query' | 'release'}[,('\\.\tape')]

    DBCC tec [( uid[, spid[, ecid]] )]

    DBCC textall [({'database_name'|database_id}[, 'FULL' | FAST] )]

    DBCC textalloc ({'table_name'|table_id}[, 'FULL' | FAST])

    DBCC thaw_io (db)

    DBCC traceoff [( tracenum [, tracenum ... ] )]

    DBCC traceon [( tracenum [, tracenum ... ] )]

    DBCC tracestatus (trace# [, ...trace#])

    DBCC unpintable (dbid, table_id)

    DBCC updateusage ({'database_name'| 0} [, 'table_name' [, index_id]])
                                            [WITH [NO_INFOMSGS] [,] COUNT_ROWS]

    DBCC upgradedb (db) DBCC usagegovernor (command, value)

    DBCC useplan [(number_of_plan)]

    DBCC useroptions DBCC wakeup (spid)

    DBCC writepage ({ dbid, 'dbname' }, fileid, pageid, offset, length, data)