Escaline

Sign in or create your account | Project List | Help

Escaline Git Source Tree

Root/brainstorming/urlparser.py

1#!/usr/bin/env python
2# coding=utf8
3#
4# escaline_parser-naps1.py
5#
6# Copyright 2008 Antoine Millet <antoine@inaps.org>
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21# MA 02110-1301, USA.
22
23import re
24
25class UrlParser:
26    ''''''
27    
28    regex_facultatives = re.compile(r'(\[(.*?)\])')
29    regex_in_facultative = re.compile(r'(\{(.*)\})')
30    regex_args = re.compile(r'([{]([^:{}]+)(?::([^{}]+))?[}])')
31
32    @staticmethod
33    def _parse_facultatives(url):
34        '''Parse all facultatives arguments in an url, and return the list.'''
35        
36        facultatives = UrlParser.regex_facultatives.findall(url)
37        
38        return facultatives
39    
40    @staticmethod
41    def _parse_facultative(facul):
42        '''Parse a facultative argument and return it's tag and name.'''
43        
44        facultative = UrlParser.regex_in_facultative.findall(facul)
45        
46        return facultative[0]
47    
48    @staticmethod
49    def _parse_arguments(url, replaces):
50        '''Parse and return all normal arguments in a three-value tuple:
51            (<argument>, <name>, <default value>)
52            
53            eg for {test:a basic test} tag:
54            
55                - argument is {test:a basic test},
56                - name is test,
57                - default is a basic test.'''
58                
59        if replaces:
60            url = UrlParser._multiple_replace(replaces, url)
61
62        arguments = UrlParser.regex_args.findall(url)
63        
64        return arguments
65    
66    @staticmethod
67    def _multiple_replace(dict, text):
68        '''Replaces in text all occurences of any key in the given dictionary by
69            its corresponding value. Returns the new string.'''
70
71        regex = re.compile('(' + '|'.join(map(re.escape, dict.keys())) + ')')
72        return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text)
73
74    
75    @staticmethod
76    def synopsis(url):
77        '''Return a string who describe using of a url-command'''
78        synopsis_facult = []
79        synopsis_facult_named = []
80        synopsis_named = []
81        synopsis_normal = []
82        
83        replaces = {}
84        
85        facultatives = UrlParser._parse_facultatives(url)
86        for facultative_all, facultative in facultatives:
87            facultative_tag, facultative_name = UrlParser._parse_facultative(facultative)
88            
89            replaces[facultative_all] = ''
90            
91            if facultative_name.isdigit():
92                synopsis_facult.append('[<#%s>]' % facultative_name)
93            else:
94                synopsis_facult_named.append('[<%s>]' % facultative_name)
95        
96        arguments = UrlParser._parse_arguments(url, replaces)
97        
98        for arg, arg_name, arg_default in arguments:
99            if arg_name.isdigit():
100                if arg_default:
101                    synopsis_normal.append('<#%s (%s)>' % (arg_name, arg_default))
102                else:
103                    synopsis_normal.append('<#%s>' % arg_name)
104            else:
105                if arg_default:
106                    synopsis_named.append('<%s (%s)>' % (arg_name, arg_default))
107                else:
108                    synopsis_named.append('<%s>' % arg_name)
109        
110        synopsis_normal.sort()
111        synopsis_facult.sort()
112        
113        return ' '.join((synopsis_facult_named + synopsis_named
114                + synopsis_normal + synopsis_facult))
115            
116    @staticmethod
117    def parse(url, args, kwargs):
118        '''Parse and replace by data provided by args list and kwargs dict
119            arguments in an url.
120            
121            Return the new url.'''
122            
123        replaces = {} # Replacement table
124        
125        facultatives = UrlParser._parse_facultatives(url) # Getting all
126        # facultatives arguments
127
128        # For each facultative argument, find its name, and search if it
129        # has a value in the kwargs dict
130        for facultative_all, facultative in facultatives:
131            facultative_tag, facultative_name = UrlParser._parse_facultative(facultative)
132            # Determines if the argument is a classic argument or a named
133            # argument
134            if facultative_name.isdigit():
135                if int(facultative_name) < len(args):
136                    # Argument exists, replace it by the arg value in list
137                    replaces[facultative_all] = facultative.replace(
138                                                    facultative_tag,
139                                                    args[int(facultative_name)]
140                                                )
141                else:
142                    # Argument doesn't exist, clearing it
143                    replaces[facultative_all] = ''
144            else: # Argument is a named argument
145                if facultative_name in kwargs:
146                    # Argument exists, replace it by the arg value in dict
147                    replaces[facultative_all] = facultative.replace(
148                                                    facultative_tag,
149                                                    kwargs[facultative_name]
150                                                )
151                else:
152                    # Argument doesn't exist, clearing it
153                    replaces[facultative_all] = ''
154        
155        # Parse the other arguments
156        arguments = UrlParser._parse_arguments(url, replaces) # Getting
157        # all the other arguments
158        
159        for arg, arg_name, arg_default in arguments:
160            # Determines if the argument is a classic argument or a named
161            # argument
162            if arg_name.isdigit():
163                if int(arg_name) < len(args):
164                    # Argument exists, replace it by the arg value
165                    replaces[arg] = args[int(arg_name)]
166                else:
167                    # Argument doesn't exist, replace it by the default value
168                    replaces[arg] = arg_default
169            else:
170                if arg_name in kwargs:
171                    replaces[arg] = kwargs[arg_name]
172                else:
173                    replaces[arg] = arg_default
174        
175        # The replacement table is filled, we can now replace :-)
176        new_url = UrlParser._multiple_replace(replaces, url)
177        
178        return new_url
179
180if __name__ == '__main__':
181    
182    # Unit testing
183    
184    print
185    print 'TESTING MODULE...'
186    
187    values = ( # ( (url, args, kwargs), result )
188        (('http://google.fr?q={0}', ('vache',), {}), 'http://google.fr?q=vache'), # One arg
189        (('http://google.fr?q={0}&qq={1}&qqq={2}', ('1234', 'abc', 'def'), {}), 'http://google.fr?q=1234&qq=abc&qqq=def'), # Many args
190        (('http://google.fr?q={0}', (), {}), 'http://google.fr?q='), # Empty args
191        (('http://google.fr?q={0:toto}', (), {}), 'http://google.fr?q=toto'), # Default value
192        (('http://google.fr?q={named}', (), {'named':'toto'}), 'http://google.fr?q=toto'), # named arg
193        (('http://google.fr?q={named:toto}', (), {'named':'toto'}), 'http://google.fr?q=toto'), # Default value on named arg
194        (('http://google.fr[?q={named}]', (), {'named':'toto'}), 'http://google.fr?q=toto'), # Optional named arg (used)
195        (('http://google.fr[?q={named}]', (), {}), 'http://google.fr'), # Optional named arg (not used)
196        (('http://google.fr[?q={0}]', ('toto',), {}), 'http://google.fr?q=toto'), # Optional not named arg (not used)
197        (('http://google.fr[?q={0}]', (), {}), 'http://google.fr'), # Optional not named arg (not used)
198        (('http://google.fr[?q={named}]&j={0}', ('titi',), {'named':'{0}'}), 'http://google.fr?q={0}&j=titi'), # Cmd injection
199    )
200    
201    errors = 0
202    
203    for input, result in values:
204        try:
205            result_real = UrlParser.parse(*input)
206        except Exception, err:
207            print 'Error while testing %s' % err
208            errors += 1
209        
210        else:
211            try:
212                assert(result_real == result)
213            except Exception, err:
214                print 'Unit test failed.\nWaited result is "%s", \nreal result is "%s".' % (result, result_real)
215                errors += 1
216            else:
217                print '.',
218    
219    if errors:
220        print
221        print '!!!!!!!!!!!!!!!!!!!!'
222        print '! UNIT TEST FAILED !'
223        print '!!!!!!!!!!!!!!!!!!!!'
224        print
225    else:
226        print
227        print 'SUCCESS !'
228        print
229

Archive Download this file