11.9. Assembler les pièces

Nous avons vu toutes les pièces nécessaires à la construction d'un client de services Web intelligent. Maintenat, voyons comment tout cela s'assemble.

Exemple 11.17. La fonction openanything

Cette fonction est définie dans openanything.py.


def openAnything(source, etag=None, lastmodified=None, agent=USER_AGENT):
    # non-HTTP code omitted for brevity
    if urlparse.urlparse(source)[0] == 'http':                                       1
        # open URL with urllib2                                                     
        request = urllib2.Request(source)                                           
        request.add_header('User-Agent', agent)                                      2
        if etag:                                                                    
            request.add_header('If-None-Match', etag)                                3
        if lastmodified:                                                            
            request.add_header('If-Modified-Since', lastmodified)                    4
        request.add_header('Accept-encoding', 'gzip')                                5
        opener = urllib2.build_opener(SmartRedirectHandler(), DefaultErrorHandler()) 6
        return opener.open(request)                                                  7
1 urlparse est un module utile pour, vous l'avez devinez, analyser des URL. Sa fonction principale, appelée également urlparse, prend une URL en paramètre et la découpe en un tuple composé de (schème, domaine, chemin, paramètres, paramètres de la chaîne de requête et identificateur de fragment). Seul le schème nous intéresse ici, pour nous assurer qu'il s'agit bien d'une URL HTTP (que urllib2 peut prendre en charge).
2 Nous nous identifions auprès du serveur HTTP avec la chaîne User-Agent passée par la fonction appelante. Si aucune chaîne User-Agent n'a été passée en paramètre, nous utilisons la valeur par défaut définie plus haut dans le module openanything.py. Nous n'utilisons jamais la valeur par défaut définie par urllib2.
3 Si nous avons une code de hachage ETag, nous l'envoyons dans l'en-tête If-None-Match.
4 Si nous avons une date de dernière modification, nous l'envoyons dans l'en-tête If-Modified-Since.
5 Nous indiquons au serveur que nous souhaitons des données compressées.
6 Nous construisons un opener d'URL qui utilise les deux gestionnaires d'URL spécialisés : SmartRedirectHandler pour gérer les redirections 301 et 302 et DefaultErrorHandler pour gérer les codes 304, 404 et les autres erreurs.
7 Et voilà ! Nous ouvrons l'URL et retournons un objet-fichier à l'appelant.

Exemple 11.18. La fonction fetch

Cette fonction est définie dans openanything.py.


def fetch(source, etag=None, last_modified=None, agent=USER_AGENT):  
    '''Fetch data and metadata from a URL, file, stream, or string'''
    result = {}                                                      
    f = openAnything(source, etag, last_modified, agent)              1
    result['data'] = f.read()                                         2
    if hasattr(f, 'headers'):                                        
        # save ETag, if the server sent one                          
        result['etag'] = f.headers.get('ETag')                        3
        # save Last-Modified header, if the server sent one          
        result['lastmodified'] = f.headers.get('Last-Modified')       4
        if f.headers.get('content-encoding', '') == 'gzip':           5
            # data came back gzip-compressed, decompress it          
            result['data'] = gzip.GzipFile(fileobj=StringIO(result['data']])).read()
    if hasattr(f, 'url'):                                             6
        result['url'] = f.url                                        
        result['status'] = 200                                       
    if hasattr(f, 'status'):                                          7
        result['status'] = f.status                                  
    f.close()                                                        
    return result                                                    
1 D'abord, nous appelons la fonction openAnything avec une URL, un code de hachage ETag, une date Last-Modified et une chaîne User-Agent.
2 Nous lisons les données retournées par le serveur. Si elles sont compressées, nous les décompresserons plus tard.
3 Nous sauvegardons le code de hachage ETag retourné par le serveur pour que l'application appelante puisse nous la passer à nouveau la prochaine fois et que nous la passions à openAnything, qui la mettra dans l'en-tête If-None-Match et l'enverra au serveur distant.
4 Nous sauvegardons aussi la date Last-Modified.
5 Si le serveur indique qu'il a envoyé des données compressées, nous les décompressons.
6 Si le serveur nous envoi une URL, nous la sauvegardons et considérons que le code de statut est 200 jusqu'à preuve du contraire.
7 Si un des gestionnaires d'URL spécialisés a obtenu un code de statut, nous le sauvegardons également.

Exemple 11.19. Utilisation de openanything.py

>>> import openanything
>>> useragent = 'MyHTTPWebServicesApp/1.0'
>>> url = 'http://diveintopython.org/redir/example301.xml'
>>> params = openanything.fetch(url, agent=useragent)              1
>>> params                                                         2
{'url': 'http://diveintomark.org/xml/atom.xml', 
'lastmodified': 'Thu, 15 Apr 2004 19:45:21 GMT', 
'etag': '"e842a-3e53-55d97640"', 
'status': 301,
'data': '<?xml version="1.0" encoding="iso-8859-1"?>
<feed version="0.3"
<-- rest of data omitted for brevity -->'}
>>> if params['status'] == 301:                                    3
...     url = params['url']
>>> newparams = openanything.fetch(
...     url, params['etag'], params['lastmodified'], useragent)    4
>>> newparams
{'url': 'http://diveintomark.org/xml/atom.xml', 
'lastmodified': None, 
'etag': '"e842a-3e53-55d97640"', 
'status': 304,
'data': ''}                                                        5
1 La toute première fois que nous allons chercher une ressource, nous n'avons pas de code de hachage ETag ni de date Last-Modified, donc nous laissons ces paramètres vides (ce sont des paramètres optionnels).
2 Ce qui nous est retourné est un dictionnaire contenant les en-têtes, le code de statut HTTP et les données retournées par le serveur. le module openanything s'occupe de la compression gzip en internet, nous n'avons donc pas à nous en occuper à ce niveau.
3 Si nous recevons un code de statut 301, c'est une redirection permanente et nous devons mettre à jour notre URL à la nouvelle adresse.
4 La deuxième fois que nous allons chercher la même ressource, nous avons toutes sortes d'information a passer en argument : une URL (éventuellement mise à jour), le ETag et la date Last-Modified de la dernière requête et bien sûr la chaîne User-Agent.
5 Ce qui nous est retourné est à nouveau un dictionnaire, mais les données n'ayant pas changé, nous n'obtenons qu'un code de statut 304 et aucune données.