web2py.gluon.widget

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

# Source Code for Module 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 sys
     13  import cStringIO
     14  import time
     15  import thread
     16  import re
     17  import os
     18  import socket
     19  import signal
     20  import math
     21  import logging
     22  import newcron
     23  import main
     24
     25  from fileutils import w2p_pack, read_file, write_file
     26  from shell import run, test
     27  from settings import global_settings
     28
     29  try:
     30      import Tkinter, tkMessageBox
     31      import contrib.taskbar_widget
     32      from winservice import web2py_windows_service_handler
     33  except:
     34      pass
     35
     36
     37  try:
     38      BaseException
     39  except NameError:
     40      BaseException = Exception
     41
     42  ProgramName = 'web2py Web Framework'
     43  ProgramAuthor = 'Created by Massimo Di Pierro, Copyright 2007-2011'
     44  ProgramVersion = read_file('VERSION').strip()
     45
     46  ProgramInfo = '''%s
     47                   %s
     48                   %s''' % (ProgramName, ProgramAuthor, ProgramVersion)
     49
     50  if not sys.version[:3] in ['2.4', '2.5', '2.6', '2.7']:
     51      msg = 'Warning: web2py requires Python 2.4, 2.5 (recommended), 2.6 or 2.7 but you are running:\n%s'
     52      msg = msg % sys.version
     53      sys.stderr.write(msg)
     54
     55  logger = logging.getLogger("web2py")
     56
     57 -class IO(object):
     58      """   """
     59
     60 -    def __init__(self):
     61          """   """
     62
     63          self.buffer = cStringIO.StringIO()
     64
     65 -    def write(self, data):
     66          """   """
     67
     68          sys.__stdout__.write(data)
     69          if hasattr(self, 'callback'):
     70              self.callback(data)
     71          else:
     72              self.buffer.write(data)
     73
     74
     75 -def try_start_browser(url):
     76      """ Try to start the default browser """
     77
     78      try:
     79          import webbrowser
     80          webbrowser.open(url)
     81      except:
     82          print 'warning: unable to detect your browser'
     83
     84
     85 -def start_browser(ip, port):
     86      """ Starts the default browser """
     87      print 'please visit:'
     88      print '\thttp://%s:%s' % (ip, port)
     89      print 'starting browser...'
     90      try_start_browser('http://%s:%s' % (ip, port))
     91
     92
     93 -def presentation(root):
     94      """ Draw the splash screen """
     95
     96      root.withdraw()
     97
     98      dx = root.winfo_screenwidth()
     99      dy = root.winfo_screenheight()
    100
    101      dialog = Tkinter.Toplevel(root, bg='white')
    102      dialog.geometry('%ix%i+%i+%i' % (500, 300, dx / 2 - 200, dy / 2 - 150))
    103
    104      dialog.overrideredirect(1)
    105      dialog.focus_force()
    106
    107      canvas = Tkinter.Canvas(dialog,
    108                              background='white',
    109                              width=500,
    110                              height=300)
    111      canvas.pack()
    112      root.update()
    113
    114      logo = 'splashlogo.gif'
    115      if os.path.exists(logo):
    116          img = Tkinter.PhotoImage(file=logo)
    117          pnl = Tkinter.Label(canvas, image=img, background='white', bd=0)
    118          pnl.pack(side='top', fill='both', expand='yes')
    119          # Prevent garbage collection of img
    120          pnl.image=img
    121
    122      def add_label(text='Change Me', font_size=12, foreground='#195866', height=1):
    123          return Tkinter.Label(
    124              master=canvas,
    125              width=250,
    126              height=height,
    127              text=text,
    128              font=('Helvetica', font_size),
    129              anchor=Tkinter.CENTER,
    130              foreground=foreground,
    131              background='white'
    132              )
    133
    134      add_label('Welcome to...').pack(side='top')
    135      add_label(ProgramName, 18, '#FF5C1F', 2).pack()
    136      add_label(ProgramAuthor).pack()
    137      add_label(ProgramVersion).pack()
    138
    139      root.update()
    140      time.sleep(5)
    141      dialog.destroy()
    142      return
    143
    144
    145 -class web2pyDialog(object):
    146      """ Main window dialog """
    147
    148 -    def __init__(self, root, options):
    149          """ web2pyDialog constructor  """
    150
    151          root.title('web2py server')
    152          self.root = Tkinter.Toplevel(root)
    153          self.options = options
    154          self.menu = Tkinter.Menu(self.root)
    155          servermenu = Tkinter.Menu(self.menu, tearoff=0)
    156          httplog = os.path.join(self.options.folder, 'httpserver.log')
    157
    158          # Building the Menu
    159          item = lambda: try_start_browser(httplog)
    160          servermenu.add_command(label='View httpserver.log',
    161                                 command=item)
    162
    163          servermenu.add_command(label='Quit (pid:%i)' % os.getpid(),
    164                                 command=self.quit)
    165
    166          self.menu.add_cascade(label='Server', menu=servermenu)
    167
    168          self.pagesmenu = Tkinter.Menu(self.menu, tearoff=0)
    169          self.menu.add_cascade(label='Pages', menu=self.pagesmenu)
    170
    171          helpmenu = Tkinter.Menu(self.menu, tearoff=0)
    172
    173          # Home Page
    174          item = lambda: try_start_browser('http://www.web2py.com')
    175          helpmenu.add_command(label='Home Page',
    176                               command=item)
    177
    178          # About
    179          item = lambda: tkMessageBox.showinfo('About web2py', ProgramInfo)
    180          helpmenu.add_command(label='About',
    181                               command=item)
    182
    183          self.menu.add_cascade(label='Info', menu=helpmenu)
    184
    185          self.root.config(menu=self.menu)
    186
    187          if options.taskbar:
    188              self.root.protocol('WM_DELETE_WINDOW',
    189                                 lambda: self.quit(True))
    190          else:
    191              self.root.protocol('WM_DELETE_WINDOW', self.quit)
    192
    193          sticky = Tkinter.NW
    194
    195          # IP
    196          Tkinter.Label(self.root,
    197                        text='Server IP:',
    198                        justify=Tkinter.LEFT).grid(row=0,
    199                                                   column=0,
    200                                                   sticky=sticky)
    201          self.ip = Tkinter.Entry(self.root)
    202          self.ip.insert(Tkinter.END, self.options.ip)
    203          self.ip.grid(row=0, column=1, sticky=sticky)
    204
    205          # Port
    206          Tkinter.Label(self.root,
    207                        text='Server Port:',
    208                        justify=Tkinter.LEFT).grid(row=1,
    209                                                   column=0,
    210                                                   sticky=sticky)
    211
    212          self.port_number = Tkinter.Entry(self.root)
    213          self.port_number.insert(Tkinter.END, self.options.port)
    214          self.port_number.grid(row=1, column=1, sticky=sticky)
    215
    216          # Password
    217          Tkinter.Label(self.root,
    218                        text='Choose Password:',
    219                        justify=Tkinter.LEFT).grid(row=2,
    220                                                   column=0,
    221                                                   sticky=sticky)
    222
    223          self.password = Tkinter.Entry(self.root, show='*')
    224          self.password.bind('<Return>', lambda e: self.start())
    225          self.password.focus_force()
    226          self.password.grid(row=2, column=1, sticky=sticky)
    227
    228          # Prepare the canvas
    229          self.canvas = Tkinter.Canvas(self.root,
    230                                       width=300,
    231                                       height=100,
    232                                       bg='black')
    233          self.canvas.grid(row=3, column=0, columnspan=2)
    234          self.canvas.after(1000, self.update_canvas)
    235
    236          # Prepare the frame
    237          frame = Tkinter.Frame(self.root)
    238          frame.grid(row=4, column=0, columnspan=2)
    239
    240          # Start button
    241          self.button_start = Tkinter.Button(frame,
    242                                             text='start server',
    243                                             command=self.start)
    244
    245          self.button_start.grid(row=0, column=0)
    246
    247          # Stop button
    248          self.button_stop = Tkinter.Button(frame,
    249                                            text='stop server',
    250                                            command=self.stop)
    251
    252          self.button_stop.grid(row=0, column=1)
    253          self.button_stop.configure(state='disabled')
    254
    255          if options.taskbar:
    256              self.tb = contrib.taskbar_widget.TaskBarIcon()
    257              self.checkTaskBar()
    258
    259              if options.password != '<ask>':
    260                  self.password.insert(0, options.password)
    261                  self.start()
    262                  self.root.withdraw()
    263          else:
    264              self.tb = None
    265
    266 -    def checkTaskBar(self):
    267          """ Check taskbar status """
    268
    269          if self.tb.status:
    270              if self.tb.status[0] == self.tb.EnumStatus.QUIT:
    271                  self.quit()
    272              elif self.tb.status[0] == self.tb.EnumStatus.TOGGLE:
    273                  if self.root.state() == 'withdrawn':
    274                      self.root.deiconify()
    275                  else:
    276                      self.root.withdraw()
    277              elif self.tb.status[0] == self.tb.EnumStatus.STOP:
    278                  self.stop()
    279              elif self.tb.status[0] == self.tb.EnumStatus.START:
    280                  self.start()
    281              elif self.tb.status[0] == self.tb.EnumStatus.RESTART:
    282                  self.stop()
    283                  self.start()
    284              del self.tb.status[0]
    285
    286          self.root.after(1000, self.checkTaskBar)
    287
    288 -    def update(self, text):
    289          """ Update app text """
    290
    291          try:
    292              self.text.configure(state='normal')
    293              self.text.insert('end', text)
    294              self.text.configure(state='disabled')
    295          except:
    296              pass  # ## this should only happen in case app is destroyed
    297
    298 -    def connect_pages(self):
    299          """ Connect pages """
    300
    301          for arq in os.listdir('applications/'):
    302              if os.path.exists('applications/%s/__init__.py' % arq):
    303                  url = self.url + '/' + arq
    304                  start_browser = lambda u = url: try_start_browser(u)
    305                  self.pagesmenu.add_command(label=url,
    306                                             command=start_browser)
    307
    308 -    def quit(self, justHide=False):
    309          """ Finish the program execution """
    310
    311          if justHide:
    312              self.root.withdraw()
    313          else:
    314              try:
    315                  self.server.stop()
    316              except:
    317                  pass
    318
    319              try:
    320                  self.tb.Destroy()
    321              except:
    322                  pass
    323
    324              self.root.destroy()
    325              sys.exit(0)
    326
    327 -    def error(self, message):
    328          """ Show error message """
    329
    330          tkMessageBox.showerror('web2py start server', message)
    331
    332 -    def start(self):
    333          """ Start web2py server """
    334
    335          password = self.password.get()
    336
    337          if not password:
    338              self.error('no password, no web admin interface')
    339
    340          ip = self.ip.get()
    341
    342          regexp = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'
    343          if ip and not re.compile(regexp).match(ip):
    344              return self.error('invalid host ip address')
    345
    346          try:
    347              port = int(self.port_number.get())
    348          except:
    349              return self.error('invalid port number')
    350
    351          self.url = 'http://%s:%s' % (ip, port)
    352          self.connect_pages()
    353          self.button_start.configure(state='disabled')
    354
    355          try:
    356              options = self.options
    357              req_queue_size = options.request_queue_size
    358              self.server = main.HttpServer(
    359                  ip,
    360                  port,
    361                  password,
    362                  pid_filename=options.pid_filename,
    363                  log_filename=options.log_filename,
    364                  profiler_filename=options.profiler_filename,
    365                  ssl_certificate=options.ssl_certificate,
    366                  ssl_private_key=options.ssl_private_key,
    367                  min_threads=options.minthreads,
    368                  max_threads=options.maxthreads,
    369                  server_name=options.server_name,
    370                  request_queue_size=req_queue_size,
    371                  timeout=options.timeout,
    372                  shutdown_timeout=options.shutdown_timeout,
    373                  path=options.folder,
    374                  interfaces=options.interfaces)
    375
    376              thread.start_new_thread(self.server.start, ())
    377          except Exception, e:
    378              self.button_start.configure(state='normal')
    379              return self.error(str(e))
    380
    381          self.button_stop.configure(state='normal')
    382
    383          if not options.taskbar:
    384              thread.start_new_thread(start_browser, (ip, port))
    385
    386          self.password.configure(state='readonly')
    387          self.ip.configure(state='readonly')
    388          self.port_number.configure(state='readonly')
    389
    390          if self.tb:
    391              self.tb.SetServerRunning()
    392
    393 -    def stop(self):
    394          """ Stop web2py server """
    395
    396          self.button_start.configure(state='normal')
    397          self.button_stop.configure(state='disabled')
    398          self.password.configure(state='normal')
    399          self.ip.configure(state='normal')
    400          self.port_number.configure(state='normal')
    401          self.server.stop()
    402
    403          if self.tb:
    404              self.tb.SetServerStopped()
    405
    406 -    def update_canvas(self):
    407          """ Update canvas """
    408
    409          try:
    410              t1 = os.path.getsize('httpserver.log')
    411          except:
    412              self.canvas.after(1000, self.update_canvas)
    413              return
    414
    415          try:
    416              fp = open('httpserver.log', 'r')
    417              fp.seek(self.t0)
    418              data = fp.read(t1 - self.t0)
    419              fp.close()
    420              value = self.p0[1:] + [10 + 90.0 / math.sqrt(1 + data.count('\n'))]
    421              self.p0 = value
    422
    423              for i in xrange(len(self.p0) - 1):
    424                  c = self.canvas.coords(self.q0[i])
    425                  self.canvas.coords(self.q0[i],
    426                                     (c[0],
    427                                      self.p0[i],
    428                                      c[2],
    429                                      self.p0[i + 1]))
    430              self.t0 = t1
    431          except BaseException:
    432              self.t0 = time.time()
    433              self.t0 = t1
    434              self.p0 = [100] * 300
    435              self.q0 = [self.canvas.create_line(i, 100, i + 1, 100,
    436                         fill='green') for i in xrange(len(self.p0) - 1)]
    437
    438          self.canvas.after(1000, self.update_canvas)
    439
    440
    441 -def console():
    442      """ Defines the behavior of the console web2py execution """
    443      import optparse
    444      import textwrap
    445
    446      usage = "python web2py.py"
    447
    448      description = """\
    449      web2py Web Framework startup script.
    450      ATTENTION: unless a password is specified (-a 'passwd') web2py will
    451      attempt to run a GUI. In this case command line options are ignored."""
    452
    453      description = textwrap.dedent(description)
    454
    455      parser = optparse.OptionParser(usage, None, optparse.Option, ProgramVersion)
    456
    457      parser.description = description
    458
    459      parser.add_option('-i',
    460                        '--ip',
    461                        default='127.0.0.1',
    462                        dest='ip',
    463                        help='ip address of the server (127.0.0.1)')
    464
    465      parser.add_option('-p',
    466                        '--port',
    467                        default='8000',
    468                        dest='port',
    469                        type='int',
    470                        help='port of server (8000)')
    471
    472      msg = 'password to be used for administration'
    473      msg += ' (use -a "<recycle>" to reuse the last password))'
    474      parser.add_option('-a',
    475                        '--password',
    476                        default='<ask>',
    477                        dest='password',
    478                        help=msg)
    479
    480      parser.add_option('-c',
    481                        '--ssl_certificate',
    482                        default='',
    483                        dest='ssl_certificate',
    484                        help='file that contains ssl certificate')
    485
    486      parser.add_option('-k',
    487                        '--ssl_private_key',
    488                        default='',
    489                        dest='ssl_private_key',
    490                        help='file that contains ssl private key')
    491
    492      parser.add_option('--ca-cert',
    493                        action='store',
    494                        dest='ssl_ca_certificate',
    495                        default=None,
    496                        help='Use this file containing the CA certificate to validate X509 certificates from clients')
    497
    498      parser.add_option('-d',
    499                        '--pid_filename',
    500                        default='httpserver.pid',
    501                        dest='pid_filename',
    502                        help='file to store the pid of the server')
    503
    504      parser.add_option('-l',
    505                        '--log_filename',
    506                        default='httpserver.log',
    507                        dest='log_filename',
    508                        help='file to log connections')
    509
    510      parser.add_option('-n',
    511                        '--numthreads',
    512                        default=None,
    513                        type='int',
    514                        dest='numthreads',
    515                        help='number of threads (deprecated)')
    516
    517      parser.add_option('--minthreads',
    518                        default=None,
    519                        type='int',
    520                        dest='minthreads',
    521                        help='minimum number of server threads')
    522
    523      parser.add_option('--maxthreads',
    524                        default=None,
    525                        type='int',
    526                        dest='maxthreads',
    527                        help='maximum number of server threads')
    528
    529      parser.add_option('-s',
    530                        '--server_name',
    531                        default=socket.gethostname(),
    532                        dest='server_name',
    533                        help='server name for the web server')
    534
    535      msg = 'max number of queued requests when server unavailable'
    536      parser.add_option('-q',
    537                        '--request_queue_size',
    538                        default='5',
    539                        type='int',
    540                        dest='request_queue_size',
    541                        help=msg)
    542
    543      parser.add_option('-o',
    544                        '--timeout',
    545                        default='10',
    546                        type='int',
    547                        dest='timeout',
    548                        help='timeout for individual request (10 seconds)')
    549
    550      parser.add_option('-z',
    551                        '--shutdown_timeout',
    552                        default='5',
    553                        type='int',
    554                        dest='shutdown_timeout',
    555                        help='timeout on shutdown of server (5 seconds)')
    556
    557      parser.add_option('--socket-timeout',
    558                        default=5,
    559                        type='int',
    560                        dest='socket_timeout',
    561                        help='timeout for socket (5 second)')
    562
    563      parser.add_option('-f',
    564                        '--folder',
    565                        default=os.getcwd(),
    566                        dest='folder',
    567                        help='folder from which to run web2py')
    568
    569      parser.add_option('-v',
    570                        '--verbose',
    571                        action='store_true',
    572                        dest='verbose',
    573                        default=False,
    574                        help='increase --test verbosity')
    575
    576      parser.add_option('-Q',
    577                        '--quiet',
    578                        action='store_true',
    579                        dest='quiet',
    580                        default=False,
    581                        help='disable all output')
    582
    583      msg = 'set debug output level (0-100, 0 means all, 100 means none;'
    584      msg += ' default is 30)'
    585      parser.add_option('-D',
    586                        '--debug',
    587                        dest='debuglevel',
    588                        default=30,
    589                        type='int',
    590                        help=msg)
    591
    592      msg = 'run web2py in interactive shell or IPython (if installed) with'
    593      msg += ' specified appname (if app does not exist it will be created).'
    594      msg += ' APPNAME like a/c/f (c,f optional)'
    595      parser.add_option('-S',
    596                        '--shell',
    597                        dest='shell',
    598                        metavar='APPNAME',
    599                        help=msg)
    600
    601      msg = 'run web2py in interactive shell or bpython (if installed) with'
    602      msg += ' specified appname (if app does not exist it will be created).'
    603      msg += '\n Use combined with --shell'
    604      parser.add_option('-B',
    605                        '--bpython',
    606                        action='store_true',
    607                        default=False,
    608                        dest='bpython',
    609                        help=msg)
    610
    611      msg = 'only use plain python shell; should be used with --shell option'
    612      parser.add_option('-P',
    613                        '--plain',
    614                        action='store_true',
    615                        default=False,
    616                        dest='plain',
    617                        help=msg)
    618
    619      msg = 'auto import model files; default is False; should be used'
    620      msg += ' with --shell option'
    621      parser.add_option('-M',
    622                        '--import_models',
    623                        action='store_true',
    624                        default=False,
    625                        dest='import_models',
    626                        help=msg)
    627
    628      msg = 'run PYTHON_FILE in web2py environment;'
    629      msg += ' should be used with --shell option'
    630      parser.add_option('-R',
    631                        '--run',
    632                        dest='run',
    633                        metavar='PYTHON_FILE',
    634                        default='',
    635                        help=msg)
    636
    637      msg = 'run scheduled tasks for the specified apps'
    638      msg += '-K app1,app2,app3'
    639      msg += 'requires a scheduler defined in the models'
    640      parser.add_option('-K',
    641                        '--scheduler',
    642                        dest='scheduler',
    643                        default=None,
    644                        help=msg)
    645
    646      msg = 'run doctests in web2py environment; ' +\
    647          'TEST_PATH like a/c/f (c,f optional)'
    648      parser.add_option('-T',
    649                        '--test',
    650                        dest='test',
    651                        metavar='TEST_PATH',
    652                        default=None,
    653                        help=msg)
    654
    655      parser.add_option('-W',
    656                        '--winservice',
    657                        dest='winservice',
    658                        default='',
    659                        help='-W install|start|stop as Windows service')
    660
    661      msg = 'trigger a cron run manually; usually invoked from a system crontab'
    662      parser.add_option('-C',
    663                        '--cron',
    664                        action='store_true',
    665                        dest='extcron',
    666                        default=False,
    667                        help=msg)
    668
    669      msg = 'triggers the use of softcron'
    670      parser.add_option('--softcron',
    671                        action='store_true',
    672                        dest='softcron',
    673                        default=False,
    674                        help=msg)
    675
    676      parser.add_option('-N',
    677                        '--no-cron',
    678                        action='store_true',
    679                        dest='nocron',
    680                        default=False,
    681                        help='do not start cron automatically')
    682
    683      parser.add_option('-J',
    684                        '--cronjob',
    685                        action='store_true',
    686                        dest='cronjob',
    687                        default=False,
    688                        help='identify cron-initiated command')
    689
    690      parser.add_option('-L',
    691                        '--config',
    692                        dest='config',
    693                        default='',
    694                        help='config file')
    695
    696      parser.add_option('-F',
    697                        '--profiler',
    698                        dest='profiler_filename',
    699                        default=None,
    700                        help='profiler filename')
    701
    702      parser.add_option('-t',
    703                        '--taskbar',
    704                        action='store_true',
    705                        dest='taskbar',
    706                        default=False,
    707                        help='use web2py gui and run in taskbar (system tray)')
    708
    709      parser.add_option('',
    710                        '--nogui',
    711                        action='store_true',
    712                        default=False,
    713                        dest='nogui',
    714                        help='text-only, no GUI')
    715
    716      parser.add_option('-A',
    717                        '--args',
    718                        action='store',
    719                        dest='args',
    720                        default=None,
    721                        help='should be followed by a list of arguments to be passed to script, to be used with -S, -A must be the last option')
    722
    723      parser.add_option('--no-banner',
    724                        action='store_true',
    725                        default=False,
    726                        dest='nobanner',
    727                        help='Do not print header banner')
    728
    729
    730      msg = 'listen on multiple addresses: "ip:port:cert:key:ca_cert;ip2:port2:cert2:key2:ca_cert2;..." (:cert:key optional; no spaces)'
    731      parser.add_option('--interfaces',
    732                        action='store',
    733                        dest='interfaces',
    734                        default=None,
    735                        help=msg)
    736
    737      if '-A' in sys.argv: k = sys.argv.index('-A')
    738      elif '--args' in sys.argv: k = sys.argv.index('--args')
    739      else: k=len(sys.argv)
    740      sys.argv, other_args = sys.argv[:k], sys.argv[k+1:]
    741      (options, args) = parser.parse_args()
    742      options.args = [options.run] + other_args
    743      global_settings.cmd_options = options
    744      global_settings.cmd_args = args
    745
    746      if options.quiet:
    747          capture = cStringIO.StringIO()
    748          sys.stdout = capture
    749          logger.setLevel(logging.CRITICAL + 1)
    750      else:
    751          logger.setLevel(options.debuglevel)
    752
    753      if options.config[-3:] == '.py':
    754          options.config = options.config[:-3]
    755
    756      if options.cronjob:
    757          global_settings.cronjob = True  # tell the world
    758          options.nocron = True   # don't start cron jobs
    759          options.plain = True    # cronjobs use a plain shell
    760
    761      options.folder = os.path.abspath(options.folder)
    762
    763      #  accept --interfaces in the form
    764      #  "ip:port:cert:key;ip2:port2;ip3:port3:cert3:key3"
    765      #  (no spaces; optional cert:key indicate SSL)
    766      if isinstance(options.interfaces, str):
    767          options.interfaces = [
    768              interface.split(':') for interface in options.interfaces.split(';')]
    769          for interface in options.interfaces:
    770              interface[1] = int(interface[1])    # numeric port
    771          options.interfaces = [
    772              tuple(interface) for interface in options.interfaces]
    773
    774      if options.numthreads is not None and options.minthreads is None:
    775          options.minthreads = options.numthreads  # legacy
    776
    777      if not options.cronjob:
    778          # If we have the applications package or if we should upgrade
    779          if not os.path.exists('applications/__init__.py'):
    780              write_file('applications/__init__.py', '')
    781
    782          if not os.path.exists('welcome.w2p') or os.path.exists('NEWINSTALL'):
    783              try:
    784                  w2p_pack('welcome.w2p','applications/welcome')
    785                  os.unlink('NEWINSTALL')
    786              except:
    787                  msg = "New installation: unable to create welcome.w2p file"
    788                  sys.stderr.write(msg)
    789
    790      return (options, args)
    791
    792 -def start_schedulers(options):
    793      apps = [app.strip() for app in options.scheduler.split(',')]
    794      try:
    795          from multiprocessing import Process
    796      except:
    797          sys.stderr.write('Sorry, -K only supported for python 2.6-2.7\n')
    798          return
    799      processes = []
    800      code = "from gluon import current; current._scheduler.loop()"
    801      for app in apps:
    802          print 'starting scheduler for "%s"...' % app
    803          args = (app,True,True,None,False,code)
    804          logging.getLogger().setLevel(logging.DEBUG)
    805          p = Process(target=run, args=args)
    806          processes.append(p)
    807          print "Currently running %s scheduler processes" % (len(processes))
    808          p.start()
    809          print "Processes started"
    810      for p in processes:
    811          try:
    812              p.join()
    813          except KeyboardInterrupt:
    814              p.terminate()
    815              p.join()
    816
    817
    818 -def start(cron=True):
    819      """ Start server  """
    820
    821      # ## get command line arguments
    822
    823      (options, args) = console()
    824
    825      if not options.nobanner:
    826          print ProgramName
    827          print ProgramAuthor
    828          print ProgramVersion
    829
    830      from dal import drivers
    831      if not options.nobanner:
    832          print 'Database drivers available: %s' % ', '.join(drivers)
    833
    834
    835      # ## if -L load options from options.config file
    836      if options.config:
    837          try:
    838              options2 = __import__(options.config, {}, {}, '')
    839          except Exception:
    840              try:
    841                  # Jython doesn't like the extra stuff
    842                  options2 = __import__(options.config)
    843              except Exception:
    844                  print 'Cannot import config file [%s]' % options.config
    845                  sys.exit(1)
    846          for key in dir(options2):
    847              if hasattr(options,key):
    848                  setattr(options,key,getattr(options2,key))
    849
    850      # ## if -T run doctests (no cron)
    851      if hasattr(options,'test') and options.test:
    852          test(options.test, verbose=options.verbose)
    853          return
    854
    855      # ## if -K
    856      if options.scheduler:
    857          try:
    858              start_schedulers(options)
    859          except KeyboardInterrupt:
    860              pass
    861          return
    862
    863      # ## if -S start interactive shell (also no cron)
    864      if options.shell:
    865          if not options.args is None:
    866              sys.argv[:] = options.args
    867          run(options.shell, plain=options.plain, bpython=options.bpython,
    868              import_models=options.import_models, startfile=options.run)
    869          return
    870
    871      # ## if -C start cron run (extcron) and exit
    872      # ## if -N or not cron disable cron in this *process*
    873      # ## if --softcron use softcron
    874      # ## use hardcron in all other cases
    875      if options.extcron:
    876          print 'Starting extcron...'
    877          global_settings.web2py_crontype = 'external'
    878          extcron = newcron.extcron(options.folder)
    879          extcron.start()
    880          extcron.join()
    881          return
    882      elif cron and not options.nocron and options.softcron:
    883          print 'Using softcron (but this is not very efficient)'
    884          global_settings.web2py_crontype = 'soft'
    885      elif cron and not options.nocron:
    886          print 'Starting hardcron...'
    887          global_settings.web2py_crontype = 'hard'
    888          newcron.hardcron(options.folder).start()
    889
    890      # ## if -W install/start/stop web2py as service
    891      if options.winservice:
    892          if os.name == 'nt':
    893              web2py_windows_service_handler(['', options.winservice],
    894                      options.config)
    895          else:
    896              print 'Error: Windows services not supported on this platform'
    897              sys.exit(1)
    898          return
    899
    900      # ## if no password provided and havetk start Tk interface
    901      # ## or start interface if we want to put in taskbar (system tray)
    902
    903      try:
    904          options.taskbar
    905      except:
    906          options.taskbar = False
    907
    908      if options.taskbar and os.name != 'nt':
    909          print 'Error: taskbar not supported on this platform'
    910          sys.exit(1)
    911
    912      root = None
    913
    914      if not options.nogui:
    915          try:
    916              import Tkinter
    917              havetk = True
    918          except ImportError:
    919              logger.warn('GUI not available because Tk library is not installed')
    920              havetk = False
    921
    922          if options.password == '<ask>' and havetk or options.taskbar and havetk:
    923              try:
    924                  root = Tkinter.Tk()
    925              except:
    926                  pass
    927
    928      if root:
    929          root.focus_force()
    930          if not options.quiet:
    931              presentation(root)
    932          master = web2pyDialog(root, options)
    933          signal.signal(signal.SIGTERM, lambda a, b: master.quit())
    934
    935          try:
    936              root.mainloop()
    937          except:
    938              master.quit()
    939
    940          sys.exit()
    941
    942      # ## if no tk and no password, ask for a password
    943
    944      if not root and options.password == '<ask>':
    945          options.password = raw_input('choose a password:')
    946
    947      if not options.password and not options.nobanner:
    948          print 'no password, no admin interface'
    949
    950      # ## start server
    951
    952      (ip, port) = (options.ip, int(options.port))
    953
    954      if not options.nobanner:
    955          print 'please visit:'
    956          print '\thttp://%s:%s' % (ip, port)
    957          print 'use "kill -SIGTERM %i" to shutdown the web2py server' % os.getpid()
    958
    959      server = main.HttpServer(ip=ip,
    960                               port=port,
    961                               password=options.password,
    962                               pid_filename=options.pid_filename,
    963                               log_filename=options.log_filename,
    964                               profiler_filename=options.profiler_filename,
    965                               ssl_certificate=options.ssl_certificate,
    966                               ssl_private_key=options.ssl_private_key,
    967                               ssl_ca_certificate=options.ssl_ca_certificate,
    968                               min_threads=options.minthreads,
    969                               max_threads=options.maxthreads,
    970                               server_name=options.server_name,
    971                               request_queue_size=options.request_queue_size,
    972                               timeout=options.timeout,
    973                               socket_timeout=options.socket_timeout,
    974                               shutdown_timeout=options.shutdown_timeout,
    975                               path=options.folder,
    976                               interfaces=options.interfaces)
    977
    978      try:
    979          server.start()
    980      except KeyboardInterrupt:
    981          server.stop()
    982      logging.shutdown()
    983