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, '' |
---|