Python

Python : imports circulaires

Derrière ce mot compliqué "circulaire" se cache l'un des effets pervers de Python. En effet, pour obtenir accès à des méthodes situées dans des fichiers différents, Python requiert que l'on importe celles-ci.

from mon_fichier import ma_methode Dans l'idée, c'est cool. Ca permet de ne charger que la méthode désirée en mémoire et évite la surcharge.

Dans la pratique, supposons le cas suivant : - Un fichier comments.py, qui contient la méthode getPost. Cette méthode permet d'obtenir le billet dans lequel a été fait le commentaire. - Un fichier posts.py, qui contient la méthode getComments. Cette méthode permet d'obtenir tous les commentaires d'un billet.

Dans comments.py, vous faites : from posts import * Avec un * parce que vous désirez pouvoir obtenir non seulement le getComments, mais également toutes les méthodes de posts, qui peuvent être aussi diverses que votre imagination le permet.

Dans posts.py, vous faites : from comments import * Le *, pour la même raison que précédemment. La, c'est balot. Mais le second import ne fonctionnera pas. Avec une erreur du genre :

NameError: name 'getPost' is not defined
Python n'arrive pas à charger la classe posts car elle a déjà été chargée auparavant.

Heureusement, nous avons une solution ! :) Ne faites pas de from class import *. Mais plutôt : import posts import comments La différence est au premier abord anodine. Nous n'aurons plus accès directement à notre méthode getPost, mais à comments.getPost. Du coup nous allons, en spécifiant l'espace de noms, pouvoir inclure nos classes en les croisant, l'une dans l'autre.

Je ne suis pas sur que cela soit très clair et je ne vois pas trop comment expliquer cela différemment. Donc un petit résumé : Si vous cherchez à importer une méthode de la classe qui a importé la méthode dans laquelle vous êtes (la, déjà, si vous êtes perdus, on est mal barrés), ne faites pas de from class import method Mais plutôt un import class. Puis appellez votre méthode avec un class.method.

Merci à Philippe et à Guillaume Ayoub (pour qui je n'ai pas de lien. Si il passe par ici, qu'il n'hésite pas à faire signe).

Envoyer un email en Python

De la même manière que en Ruby, Django propose une solution d'envoi d'email. Mais toute application Python n'utilise pas forcément Django. Et il peut arriver d'avoir besoin d'envoyer un email sans Django.

C'est ce que j'ai fait en développant l'outil de rapport de positions par email de RefStats.

Pour cela, nous allons utiliser la librairie smtplib. Commençons par importer cette librairie. import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

Puis envoyons notre email. msg = MIMEMultipart('alternative')
msg['Subject'] = 'Sujet de l\'email' msg.attach(MIMEText('contenu texte', 'text')) msg.attach(MIMEText('contenu html', 'html')) email = smtplib.SMTP() email.connect('localhost') # email d'envoi - email de réception - message email.sendmail('noreply@dmathieu.com', 'you@example.org', msg.as_string()) email.close()

Nous instancions l'email, y définissions son sujet. Ainsi que du contenu html et du contenu texte. Puis nous nous connectons au serveur smtp (ici localhost) et envoyons l'email.

PyUNO : ouvrir un document HTML et l'enregistrer en DOC

Après avoir démarré le serveur Open Office, voyons maintenant comment utiliser ce que celui-ci nous apporte.

Je ne vais pas détailler toutes les possibilitées offertes par celui-ci puisqu'elles sont simples : vous pouvez faire tout ce que vous faites avec Open Office en mode bureautique. Sauf que la, c'est du python et c'est automatique !

Commençons donc par ouvrir notre fichier.

cSourceDoc = "/path/to/your/file.html"
url = unohelper.systemPathToFileUrl(cSourceDoc)
args = (PropertyValue('FilterName', 0, 'HTML (StarWriter)', 0),)
model = desktop.loadComponentFromURL(url, "_blank", 0, args)
Nous précisons donc le nom du fichier; nous en définissons les paramètres d'ouverture en indiquant qu'il s'agit d'un document HTML. Puis nous ouvrons. Une liste de tous les FilterNames disponibles est accessibles ici. Mais, malheureusement, comme un peu tout avec PyUNO, elle est très mal documentée.

Maintenant que nous avons ouvert notre document, il faut l'enregistrer dans un autre format.

theTargetFile = '/path/to/your/file.doc'
url = unohelper.systemPathToFileUrl(theTargetFile)
args = (PropertyValue('FilterName', 0, 'MS Word 97', 0), PropertyValue('Unpacked', 0, False, 0),)</p>

<p>model.storeAsURL(url, args)
Nous définissons donc le chemin vers le fichier; le format final et enregistrons.

A cela nous ajoutons la fermeture du client.

print('Closing the client ...')
model.close(True)
ctx.ServiceManager
Et, comme d'habitude, vous pouvez voir le code complet.

PyUNO : démarrer automatiquement le serveur si il ne l'est pas

Open Office propose une solution permettant d'exécuter toutes les fonctions proposées par le logiciel de manière programmatique. Il s'agit de PyUNO. Cet article est le premier d'une série qui visera à présenter la librairie Python permettant de manipuler ce serveur Open Office.

Dans ce premier article, nous allons voir comment, dans un script Python, se connecter au serveur et le lancer si cela n'est pas déjà fait.

def start_client():
    print('Starting the client ...')
    localContext = uno.getComponentContext()
    resolver = localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext)
    return resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")
Cette méthode start_client se connecte au serveur Open Office lancé sur le port 2002. Si cela est impossible, une exception sera soulevée.

Du coup si l'on a une exception de soulevée, on peut considérer que le serveur n'est pas démarré et le lancer.

try:
    ctx = start_client()
except Exception, exc:
print('... Server not Started. Starting it ...')
    status = os.system('"' + os.path.normpath(oo_path) + '" "-accept=socket,host=localhost,port=2002;urp;StarOffice.ServiceManager" -nologo -headless -nofirststartwizard" &')
    time.sleep(2)
    ctx = start_client()
Nous démarrons le client. Si une exception est soulevée alors, nous démarrons le serveur, lui laissons deux secondes pour se lancer et redémarrons le client.

Pour que le serveur puisse démarrer, il vous faut renseigner la variable oo_path avec le chemin vers l'exécutable soffice

Sous debian avec Open Office installé via apt-get, je place :

oo_path = '/usr/bin/soffice'
Et sous Windows :
oo_path = 'C:/Program Files/OpenOffice.org 3/program/soffice.exe'

Suite à cela, vous pouvez récupérer l'instance de Open Office ouverte et la manipuler (créer un nouveau document, l'enregistrer, le modifier, lui changer son format, ...)

smgr = ctx.ServiceManager
desktop = smgr.createInstanceWithContext( "com.sun.star.frame.Desktop",ctx)

Et pour un exemple d'utilisation du client que nous venons de démarrer, vous pouvez lire l'article ouvrir un document HTML et l’enregistrer en DOC