Root/
| 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 | |
| 23 | import re |
| 24 | |
| 25 | class 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 | |
| 180 | if __name__ == '__main__': |
| 181 | |
| 182 | # Unit testing |
| 183 | |
| 184 | |
| 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 | |
| 221 | print '!!!!!!!!!!!!!!!!!!!!' |
| 222 | print '! UNIT TEST FAILED !' |
| 223 | print '!!!!!!!!!!!!!!!!!!!!' |
| 224 | |
| 225 | else: |
| 226 | |
| 227 | print 'SUCCESS !' |
| 228 | |
| 229 |
