[3] | 1 | """ |
---|
| 2 | Implementation of all of the individual 'twill' commands available through |
---|
| 3 | twill-sh. |
---|
| 4 | """ |
---|
| 5 | |
---|
| 6 | import sys |
---|
| 7 | import _mechanize_dist as mechanize |
---|
| 8 | from _mechanize_dist import ClientForm |
---|
| 9 | from _mechanize_dist._headersutil import is_html |
---|
| 10 | |
---|
| 11 | OUT=None |
---|
| 12 | ERR=sys.stderr |
---|
| 13 | |
---|
| 14 | # export: |
---|
| 15 | __all__ = ['get_browser', |
---|
| 16 | 'reset_browser', |
---|
| 17 | 'extend_with', |
---|
| 18 | 'exit', |
---|
| 19 | 'go', |
---|
| 20 | 'reload', |
---|
| 21 | 'url', |
---|
| 22 | 'code', |
---|
| 23 | 'follow', |
---|
| 24 | 'find', |
---|
| 25 | 'notfind', |
---|
| 26 | 'back', |
---|
| 27 | 'show', |
---|
| 28 | 'echo', |
---|
| 29 | 'save_html', |
---|
| 30 | 'sleep', |
---|
| 31 | 'agent', |
---|
| 32 | 'showforms', |
---|
| 33 | 'showlinks', |
---|
| 34 | 'showhistory', |
---|
| 35 | 'submit', |
---|
| 36 | 'formvalue', |
---|
| 37 | 'fv', |
---|
| 38 | 'formaction', |
---|
| 39 | 'fa', |
---|
| 40 | 'formclear', |
---|
| 41 | 'formfile', |
---|
| 42 | 'getinput', |
---|
| 43 | 'getpassword', |
---|
| 44 | 'save_cookies', |
---|
| 45 | 'load_cookies', |
---|
| 46 | 'clear_cookies', |
---|
| 47 | 'show_cookies', |
---|
| 48 | 'add_auth', |
---|
| 49 | 'run', |
---|
| 50 | 'runfile', |
---|
| 51 | 'setglobal', |
---|
| 52 | 'setlocal', |
---|
| 53 | 'debug', |
---|
| 54 | 'title', |
---|
| 55 | 'exit', |
---|
| 56 | 'config', |
---|
| 57 | 'tidy_ok', |
---|
| 58 | 'redirect_output', |
---|
| 59 | 'reset_output', |
---|
| 60 | 'redirect_error', |
---|
| 61 | 'reset_error', |
---|
| 62 | 'add_extra_header', |
---|
| 63 | 'show_extra_headers', |
---|
| 64 | 'clear_extra_headers', |
---|
| 65 | 'info' |
---|
| 66 | ] |
---|
| 67 | |
---|
| 68 | import re, getpass, time |
---|
| 69 | |
---|
| 70 | from browser import TwillBrowser |
---|
| 71 | |
---|
| 72 | from errors import TwillException, TwillAssertionError |
---|
| 73 | import utils |
---|
| 74 | from utils import set_form_control_value, run_tidy |
---|
| 75 | from namespaces import get_twill_glocals |
---|
| 76 | |
---|
| 77 | browser = TwillBrowser() |
---|
| 78 | |
---|
| 79 | def get_browser(): |
---|
| 80 | return browser |
---|
| 81 | |
---|
| 82 | def reset_browser(): |
---|
| 83 | """ |
---|
| 84 | >> reset_browser |
---|
| 85 | |
---|
| 86 | Reset the browser completely. |
---|
| 87 | """ |
---|
| 88 | global browser |
---|
| 89 | browser._browser.close() |
---|
| 90 | browser = TwillBrowser() |
---|
| 91 | |
---|
| 92 | global _options |
---|
| 93 | _options = {} |
---|
| 94 | _options.update(_orig_options) |
---|
| 95 | |
---|
| 96 | ### |
---|
| 97 | |
---|
| 98 | def exit(code="0"): |
---|
| 99 | """ |
---|
| 100 | exit [<code>] |
---|
| 101 | |
---|
| 102 | Exits twill, with the given exit code (defaults to 0, "no error"). |
---|
| 103 | """ |
---|
| 104 | raise SystemExit(int(code)) |
---|
| 105 | |
---|
| 106 | def go(url): |
---|
| 107 | """ |
---|
| 108 | >> go <url> |
---|
| 109 | |
---|
| 110 | Visit the URL given. |
---|
| 111 | """ |
---|
| 112 | browser.go(url) |
---|
| 113 | return browser.get_url() |
---|
| 114 | |
---|
| 115 | def reload(): |
---|
| 116 | """ |
---|
| 117 | >> reload |
---|
| 118 | |
---|
| 119 | Reload the current URL. |
---|
| 120 | """ |
---|
| 121 | browser.reload() |
---|
| 122 | return browser.get_url() |
---|
| 123 | |
---|
| 124 | def code(should_be): |
---|
| 125 | """ |
---|
| 126 | >> code <int> |
---|
| 127 | |
---|
| 128 | Check to make sure the response code for the last page is as given. |
---|
| 129 | """ |
---|
| 130 | should_be = int(should_be) |
---|
| 131 | if browser.get_code() != int(should_be): |
---|
| 132 | raise TwillAssertionError("code is %s != %s" % (browser.get_code(), |
---|
| 133 | should_be)) |
---|
| 134 | |
---|
| 135 | def tidy_ok(): |
---|
| 136 | """ |
---|
| 137 | >> tidy_ok |
---|
| 138 | |
---|
| 139 | Assert that 'tidy' produces no warnings or errors when run on the current |
---|
| 140 | page. |
---|
| 141 | |
---|
| 142 | If 'tidy' cannot be run, will fail silently (unless 'tidy_should_exist' |
---|
| 143 | option is true; see 'config' command). |
---|
| 144 | """ |
---|
| 145 | page = browser.get_html() |
---|
| 146 | if page is None: |
---|
| 147 | raise TwillAssertionError("not viewing HTML!") |
---|
| 148 | |
---|
| 149 | (clean_page, errors) = run_tidy(page) |
---|
| 150 | if clean_page is None: # tidy doesn't exist... |
---|
| 151 | if _options.get('tidy_should_exist'): |
---|
| 152 | raise TwillAssertionError("cannot run 'tidy'") |
---|
| 153 | elif errors: |
---|
| 154 | raise TwillAssertionError("tidy errors:\n====\n%s\n====\n" % (errors,)) |
---|
| 155 | |
---|
| 156 | # page is fine. |
---|
| 157 | |
---|
| 158 | def url(should_be): |
---|
| 159 | """ |
---|
| 160 | >> url <regexp> |
---|
| 161 | |
---|
| 162 | Check to make sure that the current URL matches the regexp. The local |
---|
| 163 | variable __match__ is set to the matching part of the URL. |
---|
| 164 | """ |
---|
| 165 | regexp = re.compile(should_be) |
---|
| 166 | current_url = browser.get_url() |
---|
| 167 | |
---|
| 168 | m = None |
---|
| 169 | if current_url is not None: |
---|
| 170 | m = regexp.search(current_url) |
---|
| 171 | else: |
---|
| 172 | current_url = '' |
---|
| 173 | |
---|
| 174 | if not m: |
---|
| 175 | raise TwillAssertionError("""\ |
---|
| 176 | current url is '%s'; |
---|
| 177 | does not match '%s' |
---|
| 178 | """ % (current_url, should_be,)) |
---|
| 179 | |
---|
| 180 | if m.groups(): |
---|
| 181 | match_str = m.group(1) |
---|
| 182 | else: |
---|
| 183 | match_str = m.group(0) |
---|
| 184 | |
---|
| 185 | global_dict, local_dict = get_twill_glocals() |
---|
| 186 | local_dict['__match__'] = match_str |
---|
| 187 | return match_str |
---|
| 188 | |
---|
| 189 | def follow(what): |
---|
| 190 | """ |
---|
| 191 | >> follow <regexp> |
---|
| 192 | |
---|
| 193 | Find the first matching link on the page & visit it. |
---|
| 194 | """ |
---|
| 195 | regexp = re.compile(what) |
---|
| 196 | link = browser.find_link(regexp) |
---|
| 197 | |
---|
| 198 | if link: |
---|
| 199 | browser.follow_link(link) |
---|
| 200 | return browser.get_url() |
---|
| 201 | |
---|
| 202 | raise TwillAssertionError("no links match to '%s'" % (what,)) |
---|
| 203 | |
---|
| 204 | def _parseFindFlags(flags): |
---|
| 205 | KNOWN_FLAGS = { |
---|
| 206 | 'i': re.IGNORECASE, |
---|
| 207 | 'm': re.MULTILINE, |
---|
| 208 | 's': re.DOTALL, |
---|
| 209 | } |
---|
| 210 | finalFlags = 0 |
---|
| 211 | for char in flags: |
---|
| 212 | try: |
---|
| 213 | finalFlags |= KNOWN_FLAGS[char] |
---|
| 214 | except IndexError: |
---|
| 215 | raise TwillAssertionError("unknown 'find' flag %r" % char) |
---|
| 216 | return finalFlags |
---|
| 217 | |
---|
| 218 | def find(what, flags=''): |
---|
| 219 | """ |
---|
| 220 | >> find <regexp> [<flags>] |
---|
| 221 | |
---|
| 222 | Succeed if the regular expression is on the page. Sets the local |
---|
| 223 | variable __match__ to the matching text. |
---|
| 224 | |
---|
| 225 | Flags is a string consisting of the following characters: |
---|
| 226 | |
---|
| 227 | * i: ignorecase |
---|
| 228 | * m: multiline |
---|
| 229 | * s: dotall |
---|
| 230 | |
---|
| 231 | For explanations of these, please see the Python re module |
---|
| 232 | documentation. |
---|
| 233 | """ |
---|
| 234 | regexp = re.compile(what, _parseFindFlags(flags)) |
---|
| 235 | page = browser.get_html() |
---|
| 236 | |
---|
| 237 | m = regexp.search(page) |
---|
| 238 | if not m: |
---|
| 239 | raise TwillAssertionError("no match to '%s'" % (what,)) |
---|
| 240 | |
---|
| 241 | if m.groups(): |
---|
| 242 | match_str = m.group(1) |
---|
| 243 | else: |
---|
| 244 | match_str = m.group(0) |
---|
| 245 | |
---|
| 246 | _, local_dict = get_twill_glocals() |
---|
| 247 | local_dict['__match__'] = match_str |
---|
| 248 | |
---|
| 249 | def notfind(what, flags=''): |
---|
| 250 | """ |
---|
| 251 | >> notfind <regexp> [<flags>] |
---|
| 252 | |
---|
| 253 | Fail if the regular expression is on the page. |
---|
| 254 | """ |
---|
| 255 | regexp = re.compile(what, _parseFindFlags(flags)) |
---|
| 256 | page = browser.get_html() |
---|
| 257 | |
---|
| 258 | if regexp.search(page): |
---|
| 259 | raise TwillAssertionError("match to '%s'" % (what,)) |
---|
| 260 | |
---|
| 261 | def back(): |
---|
| 262 | """ |
---|
| 263 | >> back |
---|
| 264 | |
---|
| 265 | Return to the previous page. |
---|
| 266 | """ |
---|
| 267 | browser.back() |
---|
| 268 | return browser.get_url() |
---|
| 269 | |
---|
| 270 | def show(): |
---|
| 271 | """ |
---|
| 272 | >> show |
---|
| 273 | |
---|
| 274 | Show the HTML for the current page. |
---|
| 275 | """ |
---|
| 276 | html = browser.get_html() |
---|
| 277 | print>>OUT, html |
---|
| 278 | return html |
---|
| 279 | |
---|
| 280 | def echo(*strs): |
---|
| 281 | """ |
---|
| 282 | >> echo <list> <of> <strings> |
---|
| 283 | |
---|
| 284 | Echo the arguments to the screen. |
---|
| 285 | """ |
---|
| 286 | strs = map(str, strs) |
---|
| 287 | s = " ".join(strs) |
---|
| 288 | print>>OUT, s |
---|
| 289 | |
---|
| 290 | def save_html(filename=None): |
---|
| 291 | """ |
---|
| 292 | >> save_html [<filename>] |
---|
| 293 | |
---|
| 294 | Save the HTML for the current page into <filename>. If no filename |
---|
| 295 | given, construct the filename from the URL. |
---|
| 296 | """ |
---|
| 297 | html = browser.get_html() |
---|
| 298 | if html is None: |
---|
| 299 | print>>OUT, "No page to save." |
---|
| 300 | return |
---|
| 301 | |
---|
| 302 | if filename is None: |
---|
| 303 | url = browser.get_url() |
---|
| 304 | url = url.split('?')[0] |
---|
| 305 | filename = url.split('/')[-1] |
---|
| 306 | if filename is "": |
---|
| 307 | filename = 'index.html' |
---|
| 308 | |
---|
| 309 | print>>OUT, "(Using filename '%s')" % (filename,) |
---|
| 310 | |
---|
| 311 | f = open(filename, 'w') |
---|
| 312 | f.write(html) |
---|
| 313 | f.close() |
---|
| 314 | |
---|
| 315 | def sleep(interval=1): |
---|
| 316 | """ |
---|
| 317 | >> sleep [<interval>] |
---|
| 318 | |
---|
| 319 | Sleep for the specified amount of time. |
---|
| 320 | If no interval is given, sleep for 1 second. |
---|
| 321 | """ |
---|
| 322 | time.sleep(float(interval)) |
---|
| 323 | |
---|
| 324 | _agent_map = dict( |
---|
| 325 | ie5='Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.1)', |
---|
| 326 | ie55='Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.1)', |
---|
| 327 | ie6='Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)', |
---|
| 328 | moz17='Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7) Gecko/20040616', |
---|
| 329 | opera7='Opera/7.0 (Windows NT 5.1; U) [en]', |
---|
| 330 | konq32='Mozilla/5.0 (compatible; Konqueror/3.2.3; Linux 2.4.14; X11; i686)', |
---|
| 331 | saf11='Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/100 (KHTML, like Gecko) Safari/100', |
---|
| 332 | aol9='Mozilla/4.0 (compatible; MSIE 5.5; AOL 9.0; Windows NT 5.1)',) |
---|
| 333 | |
---|
| 334 | def agent(what): |
---|
| 335 | """ |
---|
| 336 | >> agent <agent> |
---|
| 337 | |
---|
| 338 | Set the agent string (identifying the browser brand). |
---|
| 339 | |
---|
| 340 | Some convenient shortcuts: |
---|
| 341 | ie5, ie55, ie6, moz17, opera7, konq32, saf11, aol9. |
---|
| 342 | """ |
---|
| 343 | what = what.strip() |
---|
| 344 | agent = _agent_map.get(what, what) |
---|
| 345 | browser.set_agent_string(agent) |
---|
| 346 | |
---|
| 347 | def submit(submit_button=None): |
---|
| 348 | """ |
---|
| 349 | >> submit [<buttonspec>] |
---|
| 350 | |
---|
| 351 | Submit the current form (the one last clicked on) by clicking on the |
---|
| 352 | n'th submission button. If no "buttonspec" is given, submit the current |
---|
| 353 | form by using the last clicked submit button. |
---|
| 354 | |
---|
| 355 | The form to submit is the last form clicked on with a 'formvalue' command. |
---|
| 356 | |
---|
| 357 | The button used to submit is chosen based on 'buttonspec'. If 'buttonspec' |
---|
| 358 | is given, it's matched against buttons using the same rules that |
---|
| 359 | 'formvalue' uses. If 'buttonspec' is not given, submit uses the last |
---|
| 360 | submit button clicked on by 'formvalue'. If none can be found, |
---|
| 361 | submit submits the form with no submit button clicked. |
---|
| 362 | """ |
---|
| 363 | browser.submit(submit_button) |
---|
| 364 | |
---|
| 365 | def showforms(): |
---|
| 366 | """ |
---|
| 367 | >> showforms |
---|
| 368 | |
---|
| 369 | Show all of the forms on the current page. |
---|
| 370 | """ |
---|
| 371 | browser.showforms() |
---|
| 372 | return browser._browser.forms() |
---|
| 373 | |
---|
| 374 | def showlinks(): |
---|
| 375 | """ |
---|
| 376 | >> showlinks |
---|
| 377 | |
---|
| 378 | Show all of the links on the current page. |
---|
| 379 | """ |
---|
| 380 | browser.showlinks() |
---|
| 381 | return browser._browser.links() |
---|
| 382 | |
---|
| 383 | def showhistory(): |
---|
| 384 | """ |
---|
| 385 | >> showhistory |
---|
| 386 | |
---|
| 387 | Show the browser history (what URLs were visited). |
---|
| 388 | """ |
---|
| 389 | browser.showhistory() |
---|
| 390 | return browser._browser._history |
---|
| 391 | |
---|
| 392 | def formclear(formname): |
---|
| 393 | """ |
---|
| 394 | >> formclear <formname> |
---|
| 395 | |
---|
| 396 | Run 'clear' on all of the controls in this form. |
---|
| 397 | """ |
---|
| 398 | form = browser.get_form(formname) |
---|
| 399 | for control in form.controls: |
---|
| 400 | if control.readonly: |
---|
| 401 | continue |
---|
| 402 | |
---|
| 403 | control.clear() |
---|
| 404 | |
---|
| 405 | def formvalue(formname, fieldname, value): |
---|
| 406 | """ |
---|
| 407 | >> formvalue <formname> <field> <value> |
---|
| 408 | |
---|
| 409 | Set value of a form field. |
---|
| 410 | |
---|
| 411 | There are some ambiguities in the way formvalue deals with lists: |
---|
| 412 | 'formvalue' will *add* the given value to a list of multiple selection, |
---|
| 413 | for lists that allow it. |
---|
| 414 | |
---|
| 415 | Forms are matched against 'formname' as follows: |
---|
| 416 | 1. regexp match to actual form name; |
---|
| 417 | 2. if 'formname' is an integer, it's tried as an index. |
---|
| 418 | |
---|
| 419 | Form controls are matched against 'fieldname' as follows: |
---|
| 420 | 1. unique exact match to control name; |
---|
| 421 | 2. unique regexp match to control name; |
---|
| 422 | 3. if fieldname is an integer, it's tried as an index; |
---|
| 423 | 4. unique & exact match to submit-button values. |
---|
| 424 | |
---|
| 425 | Formvalue ignores read-only fields completely; if they're readonly, |
---|
| 426 | nothing is done, unless the config options ('config' command) are |
---|
| 427 | changed. |
---|
| 428 | |
---|
| 429 | 'formvalue' is available as 'fv' as well. |
---|
| 430 | """ |
---|
| 431 | form = browser.get_form(formname) |
---|
| 432 | if not form: |
---|
| 433 | raise TwillAssertionError("no matching forms!") |
---|
| 434 | |
---|
| 435 | control = browser.get_form_field(form, fieldname) |
---|
| 436 | |
---|
| 437 | browser.clicked(form, control) |
---|
| 438 | |
---|
| 439 | if control.readonly and _options['readonly_controls_writeable']: |
---|
| 440 | print>>OUT, 'forcing read-only form field to writeable' |
---|
| 441 | control.readonly = False |
---|
| 442 | |
---|
| 443 | if control.readonly or isinstance(control, ClientForm.IgnoreControl): |
---|
| 444 | print>>OUT, 'form field is read-only or ignorable; nothing done.' |
---|
| 445 | return |
---|
| 446 | |
---|
| 447 | if isinstance(control, ClientForm.FileControl): |
---|
| 448 | raise TwillException('form field is for file upload; use "formfile" instead') |
---|
| 449 | |
---|
| 450 | set_form_control_value(control, value) |
---|
| 451 | |
---|
| 452 | fv = formvalue |
---|
| 453 | |
---|
| 454 | def formaction(formname, action): |
---|
| 455 | """ |
---|
| 456 | >> formaction <formname> <action_url> |
---|
| 457 | |
---|
| 458 | Sets action parameter on form to action_url |
---|
| 459 | """ |
---|
| 460 | form = browser.get_form(formname) |
---|
| 461 | form.action = action |
---|
| 462 | |
---|
| 463 | fa = formaction |
---|
| 464 | |
---|
| 465 | def formfile(formname, fieldname, filename, content_type=None): |
---|
| 466 | """ |
---|
| 467 | >> formfile <form> <field> <filename> [ <content_type> ] |
---|
| 468 | |
---|
| 469 | Upload a file via an "upload file" form field. |
---|
| 470 | """ |
---|
| 471 | import os.path |
---|
| 472 | filename = filename.replace('/', os.path.sep) |
---|
| 473 | |
---|
| 474 | form = browser.get_form(formname) |
---|
| 475 | control = browser.get_form_field(form, fieldname) |
---|
| 476 | |
---|
| 477 | if not control.is_of_kind('file'): |
---|
| 478 | raise TwillException('ERROR: field is not a file upload field!') |
---|
| 479 | |
---|
| 480 | browser.clicked(form, control) |
---|
| 481 | fp = open(filename, 'rb') |
---|
| 482 | control.add_file(fp, content_type, filename) |
---|
| 483 | |
---|
| 484 | print>>OUT, '\nAdded file "%s" to file upload field "%s"\n' % (filename, |
---|
| 485 | control.name,) |
---|
| 486 | |
---|
| 487 | def extend_with(module_name): |
---|
| 488 | """ |
---|
| 489 | >> extend_with <module> |
---|
| 490 | |
---|
| 491 | Import contents of given module. |
---|
| 492 | """ |
---|
| 493 | global_dict, local_dict = get_twill_glocals() |
---|
| 494 | |
---|
| 495 | exec "from %s import *" % (module_name,) in global_dict |
---|
| 496 | |
---|
| 497 | ### now add the commands into the commands available for the shell, |
---|
| 498 | ### and print out some nice stuff about what the extension module does. |
---|
| 499 | |
---|
| 500 | import sys |
---|
| 501 | mod = sys.modules.get(module_name) |
---|
| 502 | |
---|
| 503 | ### |
---|
| 504 | |
---|
| 505 | import twill.shell, twill.parse |
---|
| 506 | |
---|
| 507 | fnlist = getattr(mod, '__all__', None) |
---|
| 508 | if fnlist is None: |
---|
| 509 | fnlist = [ fn for fn in dir(mod) if callable(getattr(mod, fn)) ] |
---|
| 510 | |
---|
| 511 | for command in fnlist: |
---|
| 512 | fn = getattr(mod, command) |
---|
| 513 | twill.shell.add_command(command, fn.__doc__) |
---|
| 514 | twill.parse.command_list.append(command) |
---|
| 515 | |
---|
| 516 | ### |
---|
| 517 | |
---|
| 518 | print>>OUT, "Imported extension module '%s'." % (module_name,) |
---|
| 519 | print>>OUT, "(at %s)\n" % (mod.__file__,) |
---|
| 520 | |
---|
| 521 | if twill.shell.interactive: |
---|
| 522 | if mod.__doc__: |
---|
| 523 | print>>OUT, "Description:\n\n%s\n" % (mod.__doc__.strip(),) |
---|
| 524 | else: |
---|
| 525 | if fnlist: |
---|
| 526 | print>>OUT, 'New commands:\n' |
---|
| 527 | for name in fnlist: |
---|
| 528 | print>>OUT, '\t', name |
---|
| 529 | |
---|
| 530 | print>>OUT, '' |
---|
| 531 | |
---|
| 532 | def getinput(prompt): |
---|
| 533 | """ |
---|
| 534 | >> getinput <prompt> |
---|
| 535 | Get input, store it in '__input__'. |
---|
| 536 | """ |
---|
| 537 | _, local_dict = get_twill_glocals() |
---|
| 538 | |
---|
| 539 | inp = raw_input(prompt) |
---|
| 540 | |
---|
| 541 | local_dict['__input__'] = inp |
---|
| 542 | return inp |
---|
| 543 | |
---|
| 544 | def getpassword(prompt): |
---|
| 545 | """ |
---|
| 546 | >> getpassword <prompt> |
---|
| 547 | |
---|
| 548 | Get a password ("invisible input"), store it in '__password__'. |
---|
| 549 | """ |
---|
| 550 | _, local_dict = get_twill_glocals() |
---|
| 551 | |
---|
| 552 | inp = getpass.getpass(prompt) |
---|
| 553 | |
---|
| 554 | local_dict['__password__'] = inp |
---|
| 555 | return inp |
---|
| 556 | |
---|
| 557 | def save_cookies(filename): |
---|
| 558 | """ |
---|
| 559 | >> save_cookies <filename> |
---|
| 560 | |
---|
| 561 | Save all of the current cookies to the given file. |
---|
| 562 | """ |
---|
| 563 | browser.save_cookies(filename) |
---|
| 564 | |
---|
| 565 | def load_cookies(filename): |
---|
| 566 | """ |
---|
| 567 | >> load_cookies <filename> |
---|
| 568 | |
---|
| 569 | Clear the cookie jar and load cookies from the given file. |
---|
| 570 | """ |
---|
| 571 | browser.load_cookies(filename) |
---|
| 572 | |
---|
| 573 | def clear_cookies(): |
---|
| 574 | """ |
---|
| 575 | >> clear_cookies |
---|
| 576 | |
---|
| 577 | Clear the cookie jar. |
---|
| 578 | """ |
---|
| 579 | browser.clear_cookies() |
---|
| 580 | |
---|
| 581 | def show_cookies(): |
---|
| 582 | """ |
---|
| 583 | >> show_cookies |
---|
| 584 | |
---|
| 585 | Show all of the cookies in the cookie jar. |
---|
| 586 | """ |
---|
| 587 | browser.show_cookies() |
---|
| 588 | |
---|
| 589 | def add_auth(realm, uri, user, passwd): |
---|
| 590 | """ |
---|
| 591 | >> add_auth <realm> <uri> <user> <passwd> |
---|
| 592 | |
---|
| 593 | Add HTTP Basic Authentication information for the given realm/uri. |
---|
| 594 | """ |
---|
| 595 | # swap around the type of HTTPPasswordMgr and |
---|
| 596 | # HTTPPasswordMgrWithDefaultRealm depending on if with_default_realm |
---|
| 597 | # is on or not. |
---|
| 598 | if _options['with_default_realm']: |
---|
| 599 | realm = None |
---|
| 600 | |
---|
| 601 | if browser.creds.__class__ == mechanize.HTTPPasswordMgr: |
---|
| 602 | passwds = browser.creds.passwd |
---|
| 603 | browser.creds = mechanize.HTTPPasswordMgrWithDefaultRealm() |
---|
| 604 | browser.creds.passwd = passwds |
---|
| 605 | print>>OUT, 'Changed to using HTTPPasswordMgrWithDefaultRealm' |
---|
| 606 | else: |
---|
| 607 | if browser.creds.__class__ == mechanize.HTTPPasswordMgrWithDefaultRealm: |
---|
| 608 | passwds = browser.creds.passwd |
---|
| 609 | browser.creds = mechanize.HTTPPasswordMgr() |
---|
| 610 | browser.creds.passwd = passwds |
---|
| 611 | print>>OUT, 'Changed to using HTTPPasswordMgr' |
---|
| 612 | |
---|
| 613 | browser.creds.add_password(realm, uri, user, passwd) |
---|
| 614 | |
---|
| 615 | print>>OUT, "Added auth info: realm '%s' / URI '%s' / user '%s'" % (realm, |
---|
| 616 | uri, |
---|
| 617 | user,) |
---|
| 618 | |
---|
| 619 | def debug(what, level): |
---|
| 620 | """ |
---|
| 621 | >> debug <what> <level> |
---|
| 622 | |
---|
| 623 | <what> can be: |
---|
| 624 | * http (any level >= 1), to display the HTTP transactions. |
---|
| 625 | * commands (any level >= 1), to display the commands being executed. |
---|
| 626 | * equiv-refresh (any level >= 1) to display HTTP-EQUIV refresh handling. |
---|
| 627 | """ |
---|
| 628 | import parse |
---|
| 629 | |
---|
| 630 | try: |
---|
| 631 | level = int(level) |
---|
| 632 | except ValueError: |
---|
| 633 | flag = utils.make_boolean(level) |
---|
| 634 | if flag: |
---|
| 635 | level = 1 |
---|
| 636 | else: |
---|
| 637 | level = 0 |
---|
| 638 | |
---|
| 639 | print>>OUT, 'DEBUG: setting %s debugging to level %d' % (what, level) |
---|
| 640 | |
---|
| 641 | if what == "http": |
---|
| 642 | browser._browser.set_debug_http(level) |
---|
| 643 | elif what == 'equiv-refresh': |
---|
| 644 | if level: |
---|
| 645 | utils._debug_print_refresh = True |
---|
| 646 | else: |
---|
| 647 | utils._debug_print_refresh = False |
---|
| 648 | elif what == 'commands': |
---|
| 649 | if level: |
---|
| 650 | parse.debug_print_commands(True) |
---|
| 651 | else: |
---|
| 652 | parse.debug_print_commands(False) |
---|
| 653 | else: |
---|
| 654 | raise TwillException('unknown debugging type: "%s"' % (what,)) |
---|
| 655 | |
---|
| 656 | def run(cmd): |
---|
| 657 | """ |
---|
| 658 | >> run <command> |
---|
| 659 | |
---|
| 660 | <command> can be any valid python command; 'exec' is used to run it. |
---|
| 661 | """ |
---|
| 662 | # @CTB: use pyparsing to grok the command? make sure that quoting works... |
---|
| 663 | |
---|
| 664 | # execute command. |
---|
| 665 | global_dict, local_dict = get_twill_glocals() |
---|
| 666 | |
---|
| 667 | import commands |
---|
| 668 | |
---|
| 669 | # set __url__ |
---|
| 670 | local_dict['__cmd__'] = cmd |
---|
| 671 | local_dict['__url__'] = commands.browser.get_url() |
---|
| 672 | |
---|
| 673 | exec(cmd, global_dict, local_dict) |
---|
| 674 | |
---|
| 675 | def runfile(*files): |
---|
| 676 | """ |
---|
| 677 | >> runfile <file1> [ <file2> ... ] |
---|
| 678 | |
---|
| 679 | """ |
---|
| 680 | import parse |
---|
| 681 | global_dict, local_dict = get_twill_glocals() |
---|
| 682 | |
---|
| 683 | for f in files: |
---|
| 684 | parse.execute_file(f, no_reset=True) |
---|
| 685 | |
---|
| 686 | def setglobal(name, value): |
---|
| 687 | """ |
---|
| 688 | setglobal <name> <value> |
---|
| 689 | |
---|
| 690 | Sets the variable <name> to the value <value> in the global namespace. |
---|
| 691 | """ |
---|
| 692 | global_dict, local_dict = get_twill_glocals() |
---|
| 693 | global_dict[name] = value |
---|
| 694 | |
---|
| 695 | def setlocal(name, value): |
---|
| 696 | """ |
---|
| 697 | setlocal <name> <value> |
---|
| 698 | |
---|
| 699 | Sets the variable <name> to the value <value> in the local namespace. |
---|
| 700 | """ |
---|
| 701 | global_dict, local_dict = get_twill_glocals() |
---|
| 702 | local_dict[name] = value |
---|
| 703 | |
---|
| 704 | def title(what): |
---|
| 705 | """ |
---|
| 706 | >> title <regexp> |
---|
| 707 | |
---|
| 708 | Succeed if the regular expression is in the page title. |
---|
| 709 | """ |
---|
| 710 | regexp = re.compile(what) |
---|
| 711 | title = browser.get_title() |
---|
| 712 | |
---|
| 713 | print>>OUT, "title is '%s'." % (title,) |
---|
| 714 | |
---|
| 715 | m = regexp.search(title) |
---|
| 716 | if not m: |
---|
| 717 | raise TwillAssertionError("title does not contain '%s'" % (what,)) |
---|
| 718 | |
---|
| 719 | if m.groups(): |
---|
| 720 | match_str = m.group(1) |
---|
| 721 | else: |
---|
| 722 | match_str = m.group(0) |
---|
| 723 | |
---|
| 724 | global_dict, local_dict = get_twill_glocals() |
---|
| 725 | local_dict['__match__'] = match_str |
---|
| 726 | return match_str |
---|
| 727 | |
---|
| 728 | def redirect_output(filename): |
---|
| 729 | """ |
---|
| 730 | >> redirect_output <filename> |
---|
| 731 | |
---|
| 732 | Append all twill output to the given file. |
---|
| 733 | """ |
---|
| 734 | import twill |
---|
| 735 | fp = open(filename, 'a') |
---|
| 736 | twill.set_output(fp) |
---|
| 737 | |
---|
| 738 | def reset_output(): |
---|
| 739 | """ |
---|
| 740 | >> reset_output |
---|
| 741 | |
---|
| 742 | Reset twill output to go to the screen. |
---|
| 743 | """ |
---|
| 744 | import twill |
---|
| 745 | twill.set_output(None) |
---|
| 746 | |
---|
| 747 | def redirect_error(filename): |
---|
| 748 | """ |
---|
| 749 | >> redirect_error <filename> |
---|
| 750 | |
---|
| 751 | Append all twill error output to the given file. |
---|
| 752 | """ |
---|
| 753 | import twill |
---|
| 754 | fp = open(filename, 'a') |
---|
| 755 | twill.set_errout(fp) |
---|
| 756 | |
---|
| 757 | def reset_error(): |
---|
| 758 | """ |
---|
| 759 | >> reset_error |
---|
| 760 | |
---|
| 761 | Reset twill error output to go to the screen. |
---|
| 762 | """ |
---|
| 763 | import twill |
---|
| 764 | twill.set_errout(None) |
---|
| 765 | |
---|
| 766 | def add_extra_header(header_key, header_value): |
---|
| 767 | """ |
---|
| 768 | >> add_header <name> <value> |
---|
| 769 | |
---|
| 770 | Add an HTTP header to each HTTP request. See 'show_extra_headers' and |
---|
| 771 | 'clear_extra_headers'. |
---|
| 772 | """ |
---|
| 773 | browser._browser.addheaders += [(header_key, header_value)] |
---|
| 774 | |
---|
| 775 | def show_extra_headers(): |
---|
| 776 | """ |
---|
| 777 | >> show_extra_headers |
---|
| 778 | |
---|
| 779 | Show any extra headers being added to each HTTP request. |
---|
| 780 | """ |
---|
| 781 | l = browser._browser.addheaders |
---|
| 782 | |
---|
| 783 | if l: |
---|
| 784 | print 'The following HTTP headers are added to each request:' |
---|
| 785 | |
---|
| 786 | for k, v in l: |
---|
| 787 | print ' "%s" = "%s"' % (k, v,) |
---|
| 788 | |
---|
| 789 | print '' |
---|
| 790 | else: |
---|
| 791 | print '** no extra HTTP headers **' |
---|
| 792 | |
---|
| 793 | def clear_extra_headers(): |
---|
| 794 | """ |
---|
| 795 | >> clear_extra_headers |
---|
| 796 | |
---|
| 797 | Remove all user-defined HTTP headers. See 'add_extra_header' and |
---|
| 798 | 'show_extra_headers'. |
---|
| 799 | """ |
---|
| 800 | browser._browser.addheaders = [] |
---|
| 801 | |
---|
| 802 | ### options |
---|
| 803 | |
---|
| 804 | _orig_options = dict(readonly_controls_writeable=False, |
---|
| 805 | use_tidy=True, |
---|
| 806 | require_tidy=False, |
---|
| 807 | use_BeautifulSoup=True, |
---|
| 808 | require_BeautifulSoup=False, |
---|
| 809 | allow_parse_errors=True, |
---|
| 810 | with_default_realm=False, |
---|
| 811 | acknowledge_equiv_refresh=True |
---|
| 812 | ) |
---|
| 813 | |
---|
| 814 | _options = {} |
---|
| 815 | _options.update(_orig_options) # make a copy |
---|
| 816 | |
---|
| 817 | def config(key=None, value=None): |
---|
| 818 | """ |
---|
| 819 | >> config [<key> [<int value>]] |
---|
| 820 | |
---|
| 821 | Configure/report various options. If no <value> is given, report |
---|
| 822 | the current key value; if no <key> given, report current settings. |
---|
| 823 | |
---|
| 824 | So far: |
---|
| 825 | |
---|
| 826 | * 'acknowledge_equiv_refresh', default 1 -- follow HTTP-EQUIV=REFRESH |
---|
| 827 | * 'readonly_controls_writeable', default 0 -- make ro controls writeable |
---|
| 828 | * 'require_tidy', default 0 -- *require* that tidy be installed |
---|
| 829 | * 'use_BeautifulSoup', default 1 -- use the BeautifulSoup parser |
---|
| 830 | * 'use_tidy', default 1 -- use tidy, if it's installed |
---|
| 831 | * 'with_default_realm', default 0 -- use a default realm for HTTP AUTH |
---|
| 832 | |
---|
| 833 | Deprecated: |
---|
| 834 | * 'allow_parse_errors' has been removed. |
---|
| 835 | """ |
---|
| 836 | import utils |
---|
| 837 | |
---|
| 838 | if key is None: |
---|
| 839 | keys = _options.keys() |
---|
| 840 | keys.sort() |
---|
| 841 | |
---|
| 842 | print>>OUT, 'current configuration:' |
---|
| 843 | for k in keys: |
---|
| 844 | print>>OUT, '\t%s : %s' % (k, _options[k]) |
---|
| 845 | print>>OUT, '' |
---|
| 846 | else: |
---|
| 847 | v = _options.get(key) |
---|
| 848 | if v is None: |
---|
| 849 | print>>OUT, '*** no such configuration key', key |
---|
| 850 | print>>OUT, 'valid keys are:', ";".join(_options.keys()) |
---|
| 851 | raise TwillException('no such configuration key: %s' % (key,)) |
---|
| 852 | elif value is None: |
---|
| 853 | print>>OUT, '' |
---|
| 854 | print>>OUT, 'key %s: value %s' % (key, v) |
---|
| 855 | print>>OUT, '' |
---|
| 856 | else: |
---|
| 857 | value = utils.make_boolean(value) |
---|
| 858 | _options[key] = value |
---|
| 859 | |
---|
| 860 | def info(): |
---|
| 861 | """ |
---|
| 862 | >> info |
---|
| 863 | |
---|
| 864 | Report information on current page. |
---|
| 865 | """ |
---|
| 866 | current_url = browser.get_url() |
---|
| 867 | if current_url is None: |
---|
| 868 | print "We're not on a page!" |
---|
| 869 | return |
---|
| 870 | |
---|
| 871 | content_type = browser._browser._response.info().getheaders("content-type") |
---|
| 872 | check_html = is_html(content_type, current_url) |
---|
| 873 | |
---|
| 874 | code = browser.get_code() |
---|
| 875 | |
---|
| 876 | |
---|
| 877 | print >>OUT, '\nPage information:' |
---|
| 878 | print >>OUT, '\tURL:', current_url |
---|
| 879 | print >>OUT, '\tHTTP code:', code |
---|
| 880 | print >>OUT, '\tContent type:', content_type[0], |
---|
| 881 | if check_html: |
---|
| 882 | print >>OUT, '(HTML)' |
---|
| 883 | else: |
---|
| 884 | print '' |
---|
| 885 | if check_html: |
---|
| 886 | title = browser.get_title() |
---|
| 887 | print >>OUT, '\tPage title:', title |
---|
| 888 | |
---|
| 889 | forms = browser.get_all_forms() |
---|
| 890 | if len(forms): |
---|
| 891 | print >>OUT, '\tThis page contains %d form(s)' % (len(forms),) |
---|
| 892 | |
---|
| 893 | print >>OUT, '' |
---|