Package Dropbox :: Package web2py :: Package gluon :: Module widget
[hide private]
[frames] | no frames]

Source Code for Module Dropbox.web2py.gluon.widget

   1  #!/usr/bin/env python 
   2  # -*- coding: utf-8 -*- 
   3   
   4  """ 
   5  This file is part of the web2py Web Framework 
   6  Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu> 
   7  License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) 
   8   
   9  The widget is called from web2py. 
  10  """ 
  11   
  12  import datetime 
  13  import sys 
  14  import cStringIO 
  15  import time 
  16  import thread 
  17  import threading 
  18  import re 
  19  import os 
  20  import socket 
  21  import signal 
  22  import math 
  23  import logging 
  24  import newcron 
  25  import getpass 
  26  import main 
  27   
  28  from fileutils import w2p_pack, read_file, write_file, create_welcome_w2p 
  29  from settings import global_settings 
  30  from shell import run, test 
  31  from utils import is_valid_ip_address, is_loopback_ip_address 
  32   
  33  try: 
  34      import Tkinter 
  35      import tkMessageBox 
  36      import contrib.taskbar_widget 
  37      from winservice import register_service_handler, Web2pyService 
  38      have_winservice = True 
  39  except: 
  40      have_winservice = False 
  41   
  42   
  43  try: 
  44      BaseException 
  45  except NameError: 
  46      BaseException = Exception 
  47   
  48  ProgramName = 'web2py Web Framework' 
  49  ProgramAuthor = 'Created by Massimo Di Pierro, Copyright 2007-' + str( 
  50      datetime.datetime.now().year) 
  51  ProgramVersion = read_file('VERSION').strip() 
  52   
  53  ProgramInfo = '''%s 
  54                   %s 
  55                   %s''' % (ProgramName, ProgramAuthor, ProgramVersion) 
  56   
  57  if not sys.version[:3] in ['2.4', '2.5', '2.6', '2.7']: 
  58      msg = 'Warning: web2py requires Python 2.4, 2.5 (recommended), 2.6 or 2.7 but you are running:\n%s' 
  59      msg = msg % sys.version 
  60      sys.stderr.write(msg) 
  61   
  62  logger = logging.getLogger("web2py") 
  63   
  64   
65 -def run_system_tests():
66 major_version = sys.version_info[0] 67 minor_version = sys.version_info[1] 68 print "minor_version = %r" % minor_version 69 if major_version == 2: 70 if minor_version in (5, 6): 71 print "Python 2.5 or 2.6" 72 ret = os.system("PYTHONPATH=. unit2 -v gluon.tests") 73 elif minor_version in (7,): 74 print "Python 2.7" 75 ret = os.system("PYTHONPATH=. python -m unittest -v gluon.tests") 76 else: 77 print "unknown python 2.x version" 78 ret = 256 79 else: 80 print "Only Python 2.x supported." 81 ret = 256 82 sys.exit(ret and 1)
83 84
85 -class IO(object):
86 """ """ 87
88 - def __init__(self):
89 """ """ 90 91 self.buffer = cStringIO.StringIO()
92
93 - def write(self, data):
94 """ """ 95 96 sys.__stdout__.write(data) 97 if hasattr(self, 'callback'): 98 self.callback(data) 99 else: 100 self.buffer.write(data)
101 102
103 -def get_url(host, path='/', proto='http', port=80):
104 if ':' in host: 105 host = '[%s]' % host 106 else: 107 host = host.replace('0.0.0.0', '127.0.0.1') 108 if path.startswith('/'): 109 path = path[1:] 110 if proto.endswith(':'): 111 proto = proto[:-1] 112 if not port or port == 80: 113 port = '' 114 else: 115 port = ':%s' % port 116 return '%s://%s%s/%s' % (proto, host, port, path)
117 118
119 -def start_browser(url, startup=False):
120 if startup: 121 print 'please visit:' 122 print '\t', url 123 print 'starting browser...' 124 try: 125 import webbrowser 126 webbrowser.open(url) 127 except: 128 print 'warning: unable to detect your browser'
129 130
131 -def presentation(root):
132 """ Draw the splash screen """ 133 134 root.withdraw() 135 136 dx = root.winfo_screenwidth() 137 dy = root.winfo_screenheight() 138 139 dialog = Tkinter.Toplevel(root, bg='white') 140 dialog.geometry('%ix%i+%i+%i' % (500, 300, dx / 2 - 200, dy / 2 - 150)) 141 142 dialog.overrideredirect(1) 143 dialog.focus_force() 144 145 canvas = Tkinter.Canvas(dialog, 146 background='white', 147 width=500, 148 height=300) 149 canvas.pack() 150 root.update() 151 152 logo = 'splashlogo.gif' 153 if os.path.exists(logo): 154 img = Tkinter.PhotoImage(file=logo) 155 pnl = Tkinter.Label(canvas, image=img, background='white', bd=0) 156 pnl.pack(side='top', fill='both', expand='yes') 157 # Prevent garbage collection of img 158 pnl.image = img 159 160 def add_label(text='Change Me', font_size=12, foreground='#195866', height=1): 161 return Tkinter.Label( 162 master=canvas, 163 width=250, 164 height=height, 165 text=text, 166 font=('Helvetica', font_size), 167 anchor=Tkinter.CENTER, 168 foreground=foreground, 169 background='white' 170 )
171 172 add_label('Welcome to...').pack(side='top') 173 add_label(ProgramName, 18, '#FF5C1F', 2).pack() 174 add_label(ProgramAuthor).pack() 175 add_label(ProgramVersion).pack() 176 177 root.update() 178 time.sleep(5) 179 dialog.destroy() 180 return 181 182
183 -class web2pyDialog(object):
184 """ Main window dialog """ 185
186 - def __init__(self, root, options):
187 """ web2pyDialog constructor """ 188 189 root.title('web2py server') 190 self.root = Tkinter.Toplevel(root) 191 self.options = options 192 self.scheduler_processes = {} 193 self.menu = Tkinter.Menu(self.root) 194 servermenu = Tkinter.Menu(self.menu, tearoff=0) 195 httplog = os.path.join(self.options.folder, 'httpserver.log') 196 197 # Building the Menu 198 item = lambda: start_browser(httplog) 199 servermenu.add_command(label='View httpserver.log', 200 command=item) 201 202 servermenu.add_command(label='Quit (pid:%i)' % os.getpid(), 203 command=self.quit) 204 205 self.menu.add_cascade(label='Server', menu=servermenu) 206 207 self.pagesmenu = Tkinter.Menu(self.menu, tearoff=0) 208 self.menu.add_cascade(label='Pages', menu=self.pagesmenu) 209 210 #scheduler menu 211 self.schedmenu = Tkinter.Menu(self.menu, tearoff=0) 212 self.menu.add_cascade(label='Scheduler', menu=self.schedmenu) 213 #start and register schedulers from options 214 self.update_schedulers(start=True) 215 216 helpmenu = Tkinter.Menu(self.menu, tearoff=0) 217 218 # Home Page 219 item = lambda: start_browser('http://www.web2py.com/') 220 helpmenu.add_command(label='Home Page', 221 command=item) 222 223 # About 224 item = lambda: tkMessageBox.showinfo('About web2py', ProgramInfo) 225 helpmenu.add_command(label='About', 226 command=item) 227 228 self.menu.add_cascade(label='Info', menu=helpmenu) 229 230 self.root.config(menu=self.menu) 231 232 if options.taskbar: 233 self.root.protocol('WM_DELETE_WINDOW', 234 lambda: self.quit(True)) 235 else: 236 self.root.protocol('WM_DELETE_WINDOW', self.quit) 237 238 sticky = Tkinter.NW 239 240 # IP 241 Tkinter.Label(self.root, 242 text='Server IP:', 243 justify=Tkinter.LEFT).grid(row=0, 244 column=0, 245 sticky=sticky) 246 self.ips = {} 247 self.selected_ip = Tkinter.StringVar() 248 row = 0 249 ips = [('127.0.0.1', 'Local (IPv4)')] + \ 250 ([('::1', 'Local (IPv6)')] if socket.has_ipv6 else []) + \ 251 [(ip, 'Public') for ip in options.ips] + \ 252 [('0.0.0.0', 'Public')] 253 for ip, legend in ips: 254 self.ips[ip] = Tkinter.Radiobutton( 255 self.root, text='%s (%s)' % (legend, ip), 256 variable=self.selected_ip, value=ip) 257 self.ips[ip].grid(row=row, column=1, sticky=sticky) 258 if row == 0: 259 self.ips[ip].select() 260 row += 1 261 shift = row 262 # Port 263 Tkinter.Label(self.root, 264 text='Server Port:', 265 justify=Tkinter.LEFT).grid(row=shift, 266 column=0, 267 sticky=sticky) 268 269 self.port_number = Tkinter.Entry(self.root) 270 self.port_number.insert(Tkinter.END, self.options.port) 271 self.port_number.grid(row=shift, column=1, sticky=sticky) 272 273 # Password 274 Tkinter.Label(self.root, 275 text='Choose Password:', 276 justify=Tkinter.LEFT).grid(row=shift + 1, 277 column=0, 278 sticky=sticky) 279 280 self.password = Tkinter.Entry(self.root, show='*') 281 self.password.bind('<Return>', lambda e: self.start()) 282 self.password.focus_force() 283 self.password.grid(row=shift + 1, column=1, sticky=sticky) 284 285 # Prepare the canvas 286 self.canvas = Tkinter.Canvas(self.root, 287 width=300, 288 height=100, 289 bg='black') 290 self.canvas.grid(row=shift + 2, column=0, columnspan=2) 291 self.canvas.after(1000, self.update_canvas) 292 293 # Prepare the frame 294 frame = Tkinter.Frame(self.root) 295 frame.grid(row=shift + 3, column=0, columnspan=2) 296 297 # Start button 298 self.button_start = Tkinter.Button(frame, 299 text='start server', 300 command=self.start) 301 302 self.button_start.grid(row=0, column=0) 303 304 # Stop button 305 self.button_stop = Tkinter.Button(frame, 306 text='stop server', 307 command=self.stop) 308 309 self.button_stop.grid(row=0, column=1) 310 self.button_stop.configure(state='disabled') 311 312 if options.taskbar: 313 self.tb = contrib.taskbar_widget.TaskBarIcon() 314 self.checkTaskBar() 315 316 if options.password != '<ask>': 317 self.password.insert(0, options.password) 318 self.start() 319 self.root.withdraw() 320 else: 321 self.tb = None
322
323 - def update_schedulers(self, start=False):
324 x = 0 325 apps = [] 326 available_apps = [arq for arq in os.listdir('applications/')] 327 available_apps = [arq for arq in available_apps 328 if os.path.exists('applications/%s/models/scheduler.py' % arq)] 329 if start: 330 #the widget takes care of starting the scheduler 331 if self.options.scheduler and self.options.with_scheduler: 332 apps = [app.strip() for app in self.options.scheduler.split(',') 333 if app in available_apps] 334 for app in apps: 335 self.try_start_scheduler(app) 336 337 #reset the menu 338 self.schedmenu.delete(0, len(available_apps)) 339 for arq in available_apps: 340 if arq not in self.scheduler_processes: 341 item = lambda u = arq: self.try_start_scheduler(u) 342 self.schedmenu.add_command(label="start %s" % arq, 343 command=item) 344 if arq in self.scheduler_processes: 345 item = lambda u = arq: self.try_stop_scheduler(u) 346 self.schedmenu.add_command(label="stop %s" % arq, 347 command=item)
348
349 - def start_schedulers(self, app):
350 try: 351 from multiprocessing import Process 352 except: 353 sys.stderr.write('Sorry, -K only supported for python 2.6-2.7\n') 354 return 355 code = "from gluon import current;current._scheduler.loop()" 356 print 'starting scheduler from widget for "%s"...' % app 357 args = (app, True, True, None, False, code) 358 logging.getLogger().setLevel(self.options.debuglevel) 359 p = Process(target=run, args=args) 360 self.scheduler_processes[app] = p 361 self.update_schedulers() 362 print "Currently running %s scheduler processes" % ( 363 len(self.scheduler_processes)) 364 p.start() 365 print "Processes started"
366
367 - def try_stop_scheduler(self, app):
368 if app in self.scheduler_processes: 369 p = self.scheduler_processes[app] 370 del self.scheduler_processes[app] 371 p.terminate() 372 p.join() 373 self.update_schedulers()
374
375 - def try_start_scheduler(self, app):
376 if app not in self.scheduler_processes: 377 t = threading.Thread(target=self.start_schedulers, args=(app,)) 378 t.start()
379
380 - def checkTaskBar(self):
381 """ Check taskbar status """ 382 383 if self.tb.status: 384 if self.tb.status[0] == self.tb.EnumStatus.QUIT: 385 self.quit() 386 elif self.tb.status[0] == self.tb.EnumStatus.TOGGLE: 387 if self.root.state() == 'withdrawn': 388 self.root.deiconify() 389 else: 390 self.root.withdraw() 391 elif self.tb.status[0] == self.tb.EnumStatus.STOP: 392 self.stop() 393 elif self.tb.status[0] == self.tb.EnumStatus.START: 394 self.start() 395 elif self.tb.status[0] == self.tb.EnumStatus.RESTART: 396 self.stop() 397 self.start() 398 del self.tb.status[0] 399 400 self.root.after(1000, self.checkTaskBar)
401
402 - def update(self, text):
403 """ Update app text """ 404 405 try: 406 self.text.configure(state='normal') 407 self.text.insert('end', text) 408 self.text.configure(state='disabled') 409 except: 410 pass # ## this should only happen in case app is destroyed
411
412 - def connect_pages(self):
413 """ Connect pages """ 414 #reset the menu 415 available_apps = [arq for arq in os.listdir('applications/') 416 if os.path.exists('applications/%s/__init__.py' % arq)] 417 self.pagesmenu.delete(0, len(available_apps)) 418 for arq in available_apps: 419 url = self.url + arq 420 self.pagesmenu.add_command(label=url, 421 command=lambda u=url: start_browser(u))
422
423 - def quit(self, justHide=False):
424 """ Finish the program execution """ 425 if justHide: 426 self.root.withdraw() 427 else: 428 try: 429 scheds = self.scheduler_processes.keys() 430 for t in scheds: 431 self.try_stop_scheduler(t) 432 except: 433 pass 434 try: 435 self.server.stop() 436 except: 437 pass 438 try: 439 self.tb.Destroy() 440 except: 441 pass 442 443 self.root.destroy() 444 sys.exit(0)
445
446 - def error(self, message):
447 """ Show error message """ 448 449 tkMessageBox.showerror('web2py start server', message)
450
451 - def start(self):
452 """ Start web2py server """ 453 454 password = self.password.get() 455 456 if not password: 457 self.error('no password, no web admin interface') 458 459 ip = self.selected_ip.get() 460 461 if not is_valid_ip_address(ip): 462 return self.error('invalid host ip address') 463 464 try: 465 port = int(self.port_number.get()) 466 except: 467 return self.error('invalid port number') 468 469 # Check for non default value for ssl inputs 470 if (len(self.options.ssl_certificate) > 0) or (len(self.options.ssl_private_key) > 0): 471 proto = 'https' 472 else: 473 proto = 'http' 474 475 self.url = get_url(ip, proto=proto, port=port) 476 self.connect_pages() 477 self.button_start.configure(state='disabled') 478 479 try: 480 options = self.options 481 req_queue_size = options.request_queue_size 482 self.server = main.HttpServer( 483 ip, 484 port, 485 password, 486 pid_filename=options.pid_filename, 487 log_filename=options.log_filename, 488 profiler_filename=options.profiler_filename, 489 ssl_certificate=options.ssl_certificate, 490 ssl_private_key=options.ssl_private_key, 491 min_threads=options.minthreads, 492 max_threads=options.maxthreads, 493 server_name=options.server_name, 494 request_queue_size=req_queue_size, 495 timeout=options.timeout, 496 shutdown_timeout=options.shutdown_timeout, 497 path=options.folder, 498 interfaces=options.interfaces) 499 500 thread.start_new_thread(self.server.start, ()) 501 except Exception, e: 502 self.button_start.configure(state='normal') 503 return self.error(str(e)) 504 505 if not self.server_ready(): 506 self.button_start.configure(state='normal') 507 return 508 509 self.button_stop.configure(state='normal') 510 511 if not options.taskbar: 512 thread.start_new_thread(start_browser, 513 (get_url(ip, proto=proto, port=port),), 514 dict(startup=True)) 515 516 self.password.configure(state='readonly') 517 [ip.configure(state='disabled') for ip in self.ips.values()] 518 self.port_number.configure(state='readonly') 519 520 if self.tb: 521 self.tb.SetServerRunning()
522
523 - def server_ready(self):
524 for listener in self.server.server.listeners: 525 if listener.ready: 526 return True 527 528 return False
529
530 - def stop(self):
531 """ Stop web2py server """ 532 533 self.button_start.configure(state='normal') 534 self.button_stop.configure(state='disabled') 535 self.password.configure(state='normal') 536 [ip.configure(state='normal') for ip in self.ips.values()] 537 self.port_number.configure(state='normal') 538 self.server.stop() 539 540 if self.tb: 541 self.tb.SetServerStopped()
542
543 - def update_canvas(self):
544 """ Update canvas """ 545 546 try: 547 t1 = os.path.getsize('httpserver.log') 548 except: 549 self.canvas.after(1000, self.update_canvas) 550 return 551 552 try: 553 fp = open('httpserver.log', 'r') 554 fp.seek(self.t0) 555 data = fp.read(t1 - self.t0) 556 fp.close() 557 value = self.p0[1:] + [10 + 90.0 / math.sqrt(1 + data.count('\n'))] 558 self.p0 = value 559 560 for i in xrange(len(self.p0) - 1): 561 c = self.canvas.coords(self.q0[i]) 562 self.canvas.coords(self.q0[i], 563 (c[0], 564 self.p0[i], 565 c[2], 566 self.p0[i + 1])) 567 self.t0 = t1 568 except BaseException: 569 self.t0 = time.time() 570 self.t0 = t1 571 self.p0 = [100] * 300 572 self.q0 = [self.canvas.create_line(i, 100, i + 1, 100, 573 fill='green') for i in xrange(len(self.p0) - 1)] 574 575 self.canvas.after(1000, self.update_canvas)
576 577
578 -def console():
579 """ Defines the behavior of the console web2py execution """ 580 import optparse 581 import textwrap 582 583 usage = "python web2py.py" 584 585 description = """\ 586 web2py Web Framework startup script. 587 ATTENTION: unless a password is specified (-a 'passwd') web2py will 588 attempt to run a GUI. In this case command line options are ignored.""" 589 590 description = textwrap.dedent(description) 591 592 parser = optparse.OptionParser( 593 usage, None, optparse.Option, ProgramVersion) 594 595 parser.description = description 596 597 msg = ('IP address of the server (e.g., 127.0.0.1 or ::1); ' 598 'Note: This value is ignored when using the \'interfaces\' option.') 599 parser.add_option('-i', 600 '--ip', 601 default='127.0.0.1', 602 dest='ip', 603 help=msg) 604 605 parser.add_option('-p', 606 '--port', 607 default='8000', 608 dest='port', 609 type='int', 610 help='port of server (8000)') 611 612 msg = ('password to be used for administration ' 613 '(use -a "<recycle>" to reuse the last password))') 614 parser.add_option('-a', 615 '--password', 616 default='<ask>', 617 dest='password', 618 help=msg) 619 620 parser.add_option('-c', 621 '--ssl_certificate', 622 default='', 623 dest='ssl_certificate', 624 help='file that contains ssl certificate') 625 626 parser.add_option('-k', 627 '--ssl_private_key', 628 default='', 629 dest='ssl_private_key', 630 help='file that contains ssl private key') 631 632 msg = ('Use this file containing the CA certificate to validate X509 ' 633 'certificates from clients') 634 parser.add_option('--ca-cert', 635 action='store', 636 dest='ssl_ca_certificate', 637 default=None, 638 help=msg) 639 640 parser.add_option('-d', 641 '--pid_filename', 642 default='httpserver.pid', 643 dest='pid_filename', 644 help='file to store the pid of the server') 645 646 parser.add_option('-l', 647 '--log_filename', 648 default='httpserver.log', 649 dest='log_filename', 650 help='file to log connections') 651 652 parser.add_option('-n', 653 '--numthreads', 654 default=None, 655 type='int', 656 dest='numthreads', 657 help='number of threads (deprecated)') 658 659 parser.add_option('--minthreads', 660 default=None, 661 type='int', 662 dest='minthreads', 663 help='minimum number of server threads') 664 665 parser.add_option('--maxthreads', 666 default=None, 667 type='int', 668 dest='maxthreads', 669 help='maximum number of server threads') 670 671 parser.add_option('-s', 672 '--server_name', 673 default=socket.gethostname(), 674 dest='server_name', 675 help='server name for the web server') 676 677 msg = 'max number of queued requests when server unavailable' 678 parser.add_option('-q', 679 '--request_queue_size', 680 default='5', 681 type='int', 682 dest='request_queue_size', 683 help=msg) 684 685 parser.add_option('-o', 686 '--timeout', 687 default='10', 688 type='int', 689 dest='timeout', 690 help='timeout for individual request (10 seconds)') 691 692 parser.add_option('-z', 693 '--shutdown_timeout', 694 default='5', 695 type='int', 696 dest='shutdown_timeout', 697 help='timeout on shutdown of server (5 seconds)') 698 699 parser.add_option('--socket-timeout', 700 default=5, 701 type='int', 702 dest='socket_timeout', 703 help='timeout for socket (5 second)') 704 705 parser.add_option('-f', 706 '--folder', 707 default=os.getcwd(), 708 dest='folder', 709 help='folder from which to run web2py') 710 711 parser.add_option('-v', 712 '--verbose', 713 action='store_true', 714 dest='verbose', 715 default=False, 716 help='increase --test verbosity') 717 718 parser.add_option('-Q', 719 '--quiet', 720 action='store_true', 721 dest='quiet', 722 default=False, 723 help='disable all output') 724 725 msg = ('set debug output level (0-100, 0 means all, 100 means none; ' 726 'default is 30)') 727 parser.add_option('-D', 728 '--debug', 729 dest='debuglevel', 730 default=30, 731 type='int', 732 help=msg) 733 734 msg = ('run web2py in interactive shell or IPython (if installed) with ' 735 'specified appname (if app does not exist it will be created). ' 736 'APPNAME like a/c/f (c,f optional)') 737 parser.add_option('-S', 738 '--shell', 739 dest='shell', 740 metavar='APPNAME', 741 help=msg) 742 743 msg = ('run web2py in interactive shell or bpython (if installed) with ' 744 'specified appname (if app does not exist it will be created).\n' 745 'Use combined with --shell') 746 parser.add_option('-B', 747 '--bpython', 748 action='store_true', 749 default=False, 750 dest='bpython', 751 help=msg) 752 753 msg = 'only use plain python shell; should be used with --shell option' 754 parser.add_option('-P', 755 '--plain', 756 action='store_true', 757 default=False, 758 dest='plain', 759 help=msg) 760 761 msg = ('auto import model files; default is False; should be used ' 762 'with --shell option') 763 parser.add_option('-M', 764 '--import_models', 765 action='store_true', 766 default=False, 767 dest='import_models', 768 help=msg) 769 770 msg = ('run PYTHON_FILE in web2py environment; ' 771 'should be used with --shell option') 772 parser.add_option('-R', 773 '--run', 774 dest='run', 775 metavar='PYTHON_FILE', 776 default='', 777 help=msg) 778 779 msg = ('run scheduled tasks for the specified apps: expects a list of ' 780 'app names as -K app1,app2,app3 ' 781 'or a list of app:groups as -K app1:group1:group2,app2:group1 ' 782 'to override specific group_names. (only strings, no spaces ' 783 'allowed. Requires a scheduler defined in the models') 784 parser.add_option('-K', 785 '--scheduler', 786 dest='scheduler', 787 default=None, 788 help=msg) 789 790 msg = 'run schedulers alongside webserver' 791 parser.add_option('-X', 792 '--with-scheduler', 793 action='store_true', 794 default=False, 795 dest='with_scheduler', 796 help=msg) 797 798 msg = ('run doctests in web2py environment; ' 799 'TEST_PATH like a/c/f (c,f optional)') 800 parser.add_option('-T', 801 '--test', 802 dest='test', 803 metavar='TEST_PATH', 804 default=None, 805 help=msg) 806 807 parser.add_option('-W', 808 '--winservice', 809 dest='winservice', 810 default='', 811 help='-W install|start|stop as Windows service') 812 813 msg = 'trigger a cron run manually; usually invoked from a system crontab' 814 parser.add_option('-C', 815 '--cron', 816 action='store_true', 817 dest='extcron', 818 default=False, 819 help=msg) 820 821 msg = 'triggers the use of softcron' 822 parser.add_option('--softcron', 823 action='store_true', 824 dest='softcron', 825 default=False, 826 help=msg) 827 828 parser.add_option('-Y', 829 '--run-cron', 830 action='store_true', 831 dest='runcron', 832 default=False, 833 help='start the background cron process') 834 835 parser.add_option('-J', 836 '--cronjob', 837 action='store_true', 838 dest='cronjob', 839 default=False, 840 help='identify cron-initiated command') 841 842 parser.add_option('-L', 843 '--config', 844 dest='config', 845 default='', 846 help='config file') 847 848 parser.add_option('-F', 849 '--profiler', 850 dest='profiler_filename', 851 default=None, 852 help='profiler filename') 853 854 parser.add_option('-t', 855 '--taskbar', 856 action='store_true', 857 dest='taskbar', 858 default=False, 859 help='use web2py gui and run in taskbar (system tray)') 860 861 parser.add_option('', 862 '--nogui', 863 action='store_true', 864 default=False, 865 dest='nogui', 866 help='text-only, no GUI') 867 868 msg = ('should be followed by a list of arguments to be passed to script, ' 869 'to be used with -S, -A must be the last option') 870 parser.add_option('-A', 871 '--args', 872 action='store', 873 dest='args', 874 default=None, 875 help=msg) 876 877 parser.add_option('--no-banner', 878 action='store_true', 879 default=False, 880 dest='nobanner', 881 help='Do not print header banner') 882 883 msg = ('listen on multiple addresses: ' 884 '"ip1:port1:key1:cert1:ca_cert1;ip2:port2:key2:cert2:ca_cert2;..." ' 885 '(:key:cert:ca_cert optional; no spaces; IPv6 addresses must be in ' 886 'square [] brackets)') 887 parser.add_option('--interfaces', 888 action='store', 889 dest='interfaces', 890 default=None, 891 help=msg) 892 893 msg = 'runs web2py tests' 894 parser.add_option('--run_system_tests', 895 action='store_true', 896 dest='run_system_tests', 897 default=False, 898 help=msg) 899 900 if '-A' in sys.argv: 901 k = sys.argv.index('-A') 902 elif '--args' in sys.argv: 903 k = sys.argv.index('--args') 904 else: 905 k = len(sys.argv) 906 sys.argv, other_args = sys.argv[:k], sys.argv[k + 1:] 907 (options, args) = parser.parse_args() 908 options.args = [options.run] + other_args 909 global_settings.cmd_options = options 910 global_settings.cmd_args = args 911 912 try: 913 options.ips = list(set([ 914 ip[4][0] for ip in socket.getaddrinfo(socket.getfqdn(), 0) 915 if not is_loopback_ip_address(ip[4][0])])) 916 except socket.gaierror: 917 options.ips = [] 918 919 if options.run_system_tests: 920 run_system_tests() 921 922 if options.quiet: 923 capture = cStringIO.StringIO() 924 sys.stdout = capture 925 logger.setLevel(logging.CRITICAL + 1) 926 else: 927 logger.setLevel(options.debuglevel) 928 929 if options.config[-3:] == '.py': 930 options.config = options.config[:-3] 931 932 if options.cronjob: 933 global_settings.cronjob = True # tell the world 934 options.plain = True # cronjobs use a plain shell 935 options.nobanner = True 936 options.nogui = True 937 938 options.folder = os.path.abspath(options.folder) 939 940 # accept --interfaces in the form 941 # "ip1:port1:key1:cert1:ca_cert1;[ip2]:port2;ip3:port3:key3:cert3" 942 # (no spaces; optional key:cert indicate SSL) 943 if isinstance(options.interfaces, str): 944 interfaces = options.interfaces.split(';') 945 options.interfaces = [] 946 for interface in interfaces: 947 if interface.startswith('['): # IPv6 948 ip, if_remainder = interface.split(']', 1) 949 ip = ip[1:] 950 if_remainder = if_remainder[1:].split(':') 951 if_remainder[0] = int(if_remainder[0]) # numeric port 952 options.interfaces.append(tuple([ip] + if_remainder)) 953 else: # IPv4 954 interface = interface.split(':') 955 interface[1] = int(interface[1]) # numeric port 956 options.interfaces.append(tuple(interface)) 957 958 # accepts --scheduler in the form 959 # "app:group1,group2,app2:group1" 960 scheduler = [] 961 options.scheduler_groups = None 962 if isinstance(options.scheduler, str): 963 if ':' in options.scheduler: 964 for opt in options.scheduler.split(','): 965 scheduler.append(opt.split(':')) 966 options.scheduler = ','.join([app[0] for app in scheduler]) 967 options.scheduler_groups = scheduler 968 969 if options.numthreads is not None and options.minthreads is None: 970 options.minthreads = options.numthreads # legacy 971 972 create_welcome_w2p() 973 974 if not options.cronjob: 975 # If we have the applications package or if we should upgrade 976 if not os.path.exists('applications/__init__.py'): 977 write_file('applications/__init__.py', '') 978 979 return (options, args)
980 981
982 -def check_existent_app(options, appname):
983 if os.path.isdir(os.path.join(options.folder, 'applications', appname)): 984 return True
985 986
987 -def get_code_for_scheduler(app, options):
988 if len(app) == 1 or app[1] is None: 989 code = "from gluon import current;current._scheduler.loop()" 990 else: 991 code = "from gluon import current;current._scheduler.group_names = ['%s'];" 992 code += "current._scheduler.loop()" 993 code = code % ("','".join(app[1:])) 994 app_ = app[0] 995 if not check_existent_app(options, app_): 996 print "Application '%s' doesn't exist, skipping" % (app_) 997 return None, None 998 return app_, code
999 1000
1001 -def start_schedulers(options):
1002 try: 1003 from multiprocessing import Process 1004 except: 1005 sys.stderr.write('Sorry, -K only supported for python 2.6-2.7\n') 1006 return 1007 processes = [] 1008 apps = [(app.strip(), None) for app in options.scheduler.split(',')] 1009 if options.scheduler_groups: 1010 apps = options.scheduler_groups 1011 code = "from gluon import current;current._scheduler.loop()" 1012 logging.getLogger().setLevel(options.debuglevel) 1013 if len(apps) == 1 and not options.with_scheduler: 1014 app_, code = get_code_for_scheduler(apps[0], options) 1015 if not app_: 1016 return 1017 print 'starting single-scheduler for "%s"...' % app_ 1018 run(app_, True, True, None, False, code) 1019 return 1020 for app in apps: 1021 app_, code = get_code_for_scheduler(app, options) 1022 if not app_: 1023 continue 1024 print 'starting scheduler for "%s"...' % app_ 1025 args = (app_, True, True, None, False, code) 1026 p = Process(target=run, args=args) 1027 processes.append(p) 1028 print "Currently running %s scheduler processes" % (len(processes)) 1029 p.start() 1030 ##to avoid bashing the db at the same time 1031 time.sleep(0.7) 1032 print "Processes started" 1033 for p in processes: 1034 try: 1035 p.join() 1036 except (KeyboardInterrupt, SystemExit): 1037 print "Processes stopped" 1038 except: 1039 p.terminate() 1040 p.join()
1041 1042
1043 -def start(cron=True):
1044 """ Start server """ 1045 1046 # ## get command line arguments 1047 1048 (options, args) = console() 1049 1050 if not options.nobanner: 1051 print ProgramName 1052 print ProgramAuthor 1053 print ProgramVersion 1054 1055 from dal import DRIVERS 1056 if not options.nobanner: 1057 print 'Database drivers available: %s' % ', '.join(DRIVERS) 1058 1059 # ## if -L load options from options.config file 1060 if options.config: 1061 try: 1062 options2 = __import__(options.config, {}, {}, '') 1063 except Exception: 1064 try: 1065 # Jython doesn't like the extra stuff 1066 options2 = __import__(options.config) 1067 except Exception: 1068 print 'Cannot import config file [%s]' % options.config 1069 sys.exit(1) 1070 for key in dir(options2): 1071 if hasattr(options, key): 1072 setattr(options, key, getattr(options2, key)) 1073 1074 if False and not os.path.exists('logging.conf') and \ 1075 os.path.exists('logging.example.conf'): 1076 import shutil 1077 sys.stdout.write("Copying logging.conf.example to logging.conf ... ") 1078 shutil.copyfile('logging.example.conf', 'logging.conf') 1079 sys.stdout.write("OK\n") 1080 1081 # ## if -T run doctests (no cron) 1082 if hasattr(options, 'test') and options.test: 1083 test(options.test, verbose=options.verbose) 1084 return 1085 1086 # ## if -S start interactive shell (also no cron) 1087 if options.shell: 1088 if not options.args is None: 1089 sys.argv[:] = options.args 1090 run(options.shell, plain=options.plain, bpython=options.bpython, 1091 import_models=options.import_models, startfile=options.run) 1092 return 1093 1094 # ## if -C start cron run (extcron) and exit 1095 # ## -K specifies optional apps list (overloading scheduler) 1096 if options.extcron: 1097 logger.debug('Starting extcron...') 1098 global_settings.web2py_crontype = 'external' 1099 if options.scheduler: # -K 1100 apps = [app.strip() for app in options.scheduler.split( 1101 ',') if check_existent_app(options, app.strip())] 1102 else: 1103 apps = None 1104 extcron = newcron.extcron(options.folder, apps=apps) 1105 extcron.start() 1106 extcron.join() 1107 return 1108 1109 # ## if -K 1110 if options.scheduler and not options.with_scheduler: 1111 try: 1112 start_schedulers(options) 1113 except KeyboardInterrupt: 1114 pass 1115 return 1116 1117 # ## if -W install/start/stop web2py as service 1118 if options.winservice: 1119 if os.name == 'nt': 1120 if have_winservice: 1121 register_service_handler( 1122 argv=['', options.winservice], 1123 opt_file=options.config, 1124 cls=Web2pyService) 1125 else: 1126 print 'Error: Missing python module winservice' 1127 sys.exit(1) 1128 else: 1129 print 'Error: Windows services not supported on this platform' 1130 sys.exit(1) 1131 return 1132 1133 # ## if -H cron is enabled in this *process* 1134 # ## if --softcron use softcron 1135 # ## use hardcron in all other cases 1136 if cron and options.runcron and options.softcron: 1137 print 'Using softcron (but this is not very efficient)' 1138 global_settings.web2py_crontype = 'soft' 1139 elif cron and options.runcron: 1140 logger.debug('Starting hardcron...') 1141 global_settings.web2py_crontype = 'hard' 1142 newcron.hardcron(options.folder).start() 1143 1144 # ## if no password provided and havetk start Tk interface 1145 # ## or start interface if we want to put in taskbar (system tray) 1146 1147 try: 1148 options.taskbar 1149 except: 1150 options.taskbar = False 1151 1152 if options.taskbar and os.name != 'nt': 1153 print 'Error: taskbar not supported on this platform' 1154 sys.exit(1) 1155 1156 root = None 1157 1158 if not options.nogui: 1159 try: 1160 import Tkinter 1161 havetk = True 1162 except ImportError: 1163 logger.warn( 1164 'GUI not available because Tk library is not installed') 1165 havetk = False 1166 1167 if options.password == '<ask>' and havetk or options.taskbar and havetk: 1168 try: 1169 root = Tkinter.Tk() 1170 except: 1171 pass 1172 1173 if root: 1174 root.focus_force() 1175 1176 # Mac OS X - make the GUI window rise to the top 1177 if os.path.exists("/usr/bin/osascript"): 1178 applescript = """ 1179 tell application "System Events" 1180 set proc to first process whose unix id is %d 1181 set frontmost of proc to true 1182 end tell 1183 """ % (os.getpid()) 1184 os.system("/usr/bin/osascript -e '%s'" % applescript) 1185 1186 if not options.quiet: 1187 presentation(root) 1188 master = web2pyDialog(root, options) 1189 signal.signal(signal.SIGTERM, lambda a, b: master.quit()) 1190 1191 try: 1192 root.mainloop() 1193 except: 1194 master.quit() 1195 1196 sys.exit() 1197 1198 # ## if no tk and no password, ask for a password 1199 1200 if not root and options.password == '<ask>': 1201 options.password = getpass.getpass('choose a password:') 1202 1203 if not options.password and not options.nobanner: 1204 print 'no password, no admin interface' 1205 1206 # ##-X (if no tk, the widget takes care of it himself) 1207 if not root and options.scheduler and options.with_scheduler: 1208 t = threading.Thread(target=start_schedulers, args=(options,)) 1209 t.start() 1210 1211 # ## start server 1212 1213 # Use first interface IP and port if interfaces specified, since the 1214 # interfaces option overrides the IP (and related) options. 1215 if not options.interfaces: 1216 (ip, port) = (options.ip, int(options.port)) 1217 else: 1218 first_if = options.interfaces[0] 1219 (ip, port) = first_if[0], first_if[1] 1220 1221 # Check for non default value for ssl inputs 1222 if (len(options.ssl_certificate) > 0) or (len(options.ssl_private_key) > 0): 1223 proto = 'https' 1224 else: 1225 proto = 'http' 1226 1227 url = get_url(ip, proto=proto, port=port) 1228 1229 if not options.nobanner: 1230 print 'please visit:' 1231 print '\t', url 1232 print 'use "kill -SIGTERM %i" to shutdown the web2py server' % os.getpid() 1233 1234 server = main.HttpServer(ip=ip, 1235 port=port, 1236 password=options.password, 1237 pid_filename=options.pid_filename, 1238 log_filename=options.log_filename, 1239 profiler_filename=options.profiler_filename, 1240 ssl_certificate=options.ssl_certificate, 1241 ssl_private_key=options.ssl_private_key, 1242 ssl_ca_certificate=options.ssl_ca_certificate, 1243 min_threads=options.minthreads, 1244 max_threads=options.maxthreads, 1245 server_name=options.server_name, 1246 request_queue_size=options.request_queue_size, 1247 timeout=options.timeout, 1248 socket_timeout=options.socket_timeout, 1249 shutdown_timeout=options.shutdown_timeout, 1250 path=options.folder, 1251 interfaces=options.interfaces) 1252 1253 try: 1254 server.start() 1255 except KeyboardInterrupt: 1256 server.stop() 1257 try: 1258 t.join() 1259 except: 1260 pass 1261 logging.shutdown()
1262