Esempio n. 1
0
def wsgibase(environ, responder):
    """
    this is the gluon wsgi application. the first function called when a page
    is requested (static or dynamic). it can be called by paste.httpserver
    or by apache mod_wsgi.

      - fills request with info
      - the environment variables, replacing '.' with '_'
      - adds web2py path and version info
      - compensates for fcgi missing path_info and query_string
      - validates the path in url

    The url path must be either:

    1. for static pages:

      - /<application>/static/<file>

    2. for dynamic pages:

      - /<application>[/<controller>[/<function>[/<sub>]]][.<extension>]
      - (sub may go several levels deep, currently 3 levels are supported:
         sub1/sub2/sub3)

    The naming conventions are:

      - application, controller, function and extension may only contain
        [a-zA-Z0-9_]
      - file and sub may also contain '-', '=', '.' and '/'
    """
    eget = environ.get
    current.__dict__.clear()
    request = Request(environ)
    response = Response()
    session = Session()
    env = request.env
    #env.web2py_path = global_settings.applications_parent
    env.web2py_version = web2py_version
    #env.update(global_settings)
    static_file = False
    try:
        try:
            try:
                # ##################################################
                # handle fcgi missing path_info and query_string
                # select rewrite parameters
                # rewrite incoming URL
                # parse rewritten header variables
                # parse rewritten URL
                # serve file if static
                # ##################################################

                fixup_missing_path_info(environ)
                (static_file, version, environ) = url_in(request, environ)
                response.status = env.web2py_status_code or response.status

                if static_file:
                    if eget('QUERY_STRING', '').startswith('attachment'):
                        response.headers['Content-Disposition'] \
                            = 'attachment'
                    if version:
                        response.headers['Cache-Control'] = 'max-age=315360000'
                        response.headers[
                            'Expires'] = 'Thu, 31 Dec 2037 23:59:59 GMT'
                    response.stream(static_file, request=request)


                # ##################################################
                # fill in request items
                # ##################################################
                app = request.application  # must go after url_in!

                if not global_settings.local_hosts:
                    local_hosts = set(['127.0.0.1', '::ffff:127.0.0.1', '::1'])
                    if not global_settings.web2py_runtime_gae:
                        try:
                            fqdn = socket.getfqdn()
                            local_hosts.add(socket.gethostname())
                            local_hosts.add(fqdn)
                            local_hosts.update([
                                addrinfo[4][0] for addrinfo
                                in getipaddrinfo(fqdn)])
                            if env.server_name:
                                local_hosts.add(env.server_name)
                                local_hosts.update([
                                        addrinfo[4][0] for addrinfo
                                        in getipaddrinfo(env.server_name)])
                        except (socket.gaierror, TypeError):
                            pass
                    global_settings.local_hosts = list(local_hosts)
                else:
                    local_hosts = global_settings.local_hosts
                client = get_client(env)
                x_req_with = str(env.http_x_requested_with).lower()

                request.update(
                    client = client,
                    folder = abspath('applications', app) + os.sep,
                    ajax = x_req_with == 'xmlhttprequest',
                    cid = env.http_web2py_component_element,
                    is_local = env.remote_addr in local_hosts,
                    is_https = env.wsgi_url_scheme in HTTPS_SCHEMES or \
                        request.env.http_x_forwarded_proto in HTTPS_SCHEMES \
                        or env.https == 'on'
                    )
                request.compute_uuid()  # requires client
                request.url = environ['PATH_INFO']

                # ##################################################
                # access the requested application
                # ##################################################

                disabled = pjoin(request.folder, 'DISABLED')
                if not exists(request.folder):
                    if app == rwthread.routes.default_application \
                            and app != 'welcome':
                        redirect(URL('welcome', 'default', 'index'))
                    elif rwthread.routes.error_handler:
                        _handler = rwthread.routes.error_handler
                        redirect(URL(_handler['application'],
                                     _handler['controller'],
                                     _handler['function'],
                                     args=app))
                    else:
                        raise HTTP(404, rwthread.routes.error_message
                                   % 'invalid request',
                                   web2py_error='invalid application')
                elif not request.is_local and exists(disabled):
                    raise HTTP(503, "<html><body><h1>Temporarily down for maintenance</h1></body></html>")

                # ##################################################
                # build missing folders
                # ##################################################

                create_missing_app_folders(request)

                # ##################################################
                # get the GET and POST data
                # ##################################################

                #parse_get_post_vars(request, environ)

                # ##################################################
                # expose wsgi hooks for convenience
                # ##################################################

                request.wsgi = LazyWSGI(environ, request, response)

                # ##################################################
                # load cookies
                # ##################################################

                if env.http_cookie:
                    try:
                        request.cookies.load(env.http_cookie)
                    except Cookie.CookieError, e:
                        pass  # invalid cookies

                # ##################################################
                # try load session or create new session file
                # ##################################################

                if not env.web2py_disable_session:
                    session.connect(request, response)

                # ##################################################
                # run controller
                # ##################################################

                if global_settings.debugging and app != "admin":
                    import gluon.debug
                    # activate the debugger
                    gluon.debug.dbg.do_debug(mainpyfile=request.folder)

                serve_controller(request, response, session)

            except HTTP, http_response:

                if static_file:
                    return http_response.to(responder, env=env)

                if request.body:
                    request.body.close()

                if hasattr(current,'request'):

                    # ##################################################
                    # on success, try store session in database
                    # ##################################################
                    session._try_store_in_db(request, response)

                    # ##################################################
                    # on success, commit database
                    # ##################################################

                    if response.do_not_commit is True:
                        BaseAdapter.close_all_instances(None)
                    elif response.custom_commit:
                        BaseAdapter.close_all_instances(response.custom_commit)
                    else:
                        BaseAdapter.close_all_instances('commit')

                    # ##################################################
                    # if session not in db try store session on filesystem
                    # this must be done after trying to commit database!
                    # ##################################################

                    session._try_store_in_cookie_or_file(request, response)

                    # Set header so client can distinguish component requests.
                    if request.cid:
                        http_response.headers.setdefault(
                            'web2py-component-content', 'replace')

                    if request.ajax:
                        if response.flash:
                            http_response.headers['web2py-component-flash'] = \
                                urllib2.quote(xmlescape(response.flash)\
                                                  .replace('\n',''))
                        if response.js:
                            http_response.headers['web2py-component-command'] = \
                                urllib2.quote(response.js.replace('\n',''))

                    # ##################################################
                    # store cookies in headers
                    # ##################################################

                    session._fixup_before_save()
                    http_response.cookies2headers(response.cookies)

                ticket = None

            except RestrictedError, e:

                if request.body:
                    request.body.close()

                # ##################################################
                # on application error, rollback database
                # ##################################################

                # log tickets before rollback if not in DB
                if not request.tickets_db:
                    ticket = e.log(request) or 'unknown'
                # rollback
                if response._custom_rollback:
                    response._custom_rollback()
                else:
                    BaseAdapter.close_all_instances('rollback')
                # if tickets in db, reconnect and store it in db
                if request.tickets_db:
                    ticket = e.log(request) or 'unknown'

                http_response = \
                    HTTP(500, rwthread.routes.error_message_ticket %
                         dict(ticket=ticket),
                         web2py_error='ticket %s' % ticket)
Esempio n. 2
0
    def __init__(self, root, options):
        """ web2pyDialog constructor  """

        if PY2:
            import Tkinter as tkinter
            import tkMessageBox as messagebox
        else:
            import tkinter
            from tkinter import messagebox

        root.withdraw()

        bg_color = 'white'
        self.root = tkinter.Toplevel(root, bg=bg_color)
        self.root.resizable(0, 0)
        self.root.title(ProgramName)

        self.options = options
        self.scheduler_processes_lock = threading.RLock()
        self.scheduler_processes = OrderedDict()

        iconphoto = os.path.join('extras', 'icons', 'web2py.gif')
        if os.path.exists(iconphoto):
            img = tkinter.PhotoImage(file=iconphoto)
            self.root.tk.call('wm', 'iconphoto', self.root._w, img)

        # Building the Menu
        self.menu = tkinter.Menu(self.root)
        servermenu = tkinter.Menu(self.menu, tearoff=0)

        httplog = os.path.join(options.folder, options.log_filename)
        item = lambda: start_browser(httplog)
        servermenu.add_command(label='View httpserver.log',
                               command=item)

        servermenu.add_command(label='Quit (pid:%i)' % os.getpid(),
                               command=self.quit)

        self.menu.add_cascade(label='Server', menu=servermenu)

        self.pagesmenu = tkinter.Menu(self.menu, tearoff=0)
        self.menu.add_cascade(label='Pages', menu=self.pagesmenu)

        self.schedmenu = tkinter.Menu(self.menu, tearoff=0)
        self.menu.add_cascade(label='Scheduler', menu=self.schedmenu)
        # register and start schedulers
        self.update_schedulers(start=True)

        helpmenu = tkinter.Menu(self.menu, tearoff=0)

        # Home Page
        item = lambda: start_browser('http://www.web2py.com/')
        helpmenu.add_command(label='Home Page',
                             command=item)

        # About
        ProgramInfo = """%s
                 %s
                 %s""" % (ProgramName, ProgramAuthor, ProgramVersion)
        item = lambda: messagebox.showinfo('About web2py', ProgramInfo)
        helpmenu.add_command(label='About',
                             command=item)

        self.menu.add_cascade(label='Info', menu=helpmenu)

        self.root.config(menu=self.menu)

        if options.taskbar:
            self.root.protocol('WM_DELETE_WINDOW',
                               lambda: self.quit(True))
        else:
            self.root.protocol('WM_DELETE_WINDOW', self.quit)

        sticky = tkinter.NW

        # Prepare the logo area
        self.logoarea = tkinter.Canvas(self.root,
                                background=bg_color,
                                width=300,
                                height=300)
        self.logoarea.grid(row=0, column=0, columnspan=4, sticky=sticky)
        self.logoarea.after(1000, self.update_canvas)

        logo = os.path.join('extras', 'icons', 'splashlogo.gif')
        if os.path.exists(logo):
            img = tkinter.PhotoImage(file=logo)
            pnl = tkinter.Label(self.logoarea, image=img, background=bg_color, bd=0)
            pnl.pack(side='top', fill='both', expand='yes')
            # Prevent garbage collection of img
            pnl.image = img

        # Prepare the banner area
        self.bannerarea = tkinter.Canvas(self.root,
                                bg=bg_color,
                                width=300,
                                height=300)
        self.bannerarea.grid(row=1, column=1, columnspan=2, sticky=sticky)

        tkinter.Label(self.bannerarea, anchor=tkinter.N,
                      text=str(ProgramVersion + "\n" + ProgramAuthor),
                      font=('Helvetica', 11), justify=tkinter.CENTER,
                      foreground='#195866', background=bg_color,
                      height=3).pack(side='top',
                                     fill='both',
                                     expand='yes')

        self.bannerarea.after(1000, self.update_canvas)

        # IP
        # retrieves the list of server IP addresses
        try:
            if_ips = list(set(  # no duplicates
                [addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn())
                 if not is_loopback_ip_address(addrinfo=addrinfo)]))
        except socket.gaierror:
            if_ips = []

        tkinter.Label(self.root,
                      text='Server IP:', bg=bg_color,
                      justify=tkinter.RIGHT).grid(row=4,
                                                  column=1,
                                                  sticky=sticky)
        self.ips = {}
        self.selected_ip = tkinter.StringVar()
        row = 4
        ips = [('127.0.0.1', 'Local (IPv4)')] + \
            ([('::1', 'Local (IPv6)')] if socket.has_ipv6 else []) + \
            [(ip, 'Public') for ip in if_ips] + \
            [('0.0.0.0', 'Public')]
        for ip, legend in ips:
            self.ips[ip] = tkinter.Radiobutton(
                self.root, bg=bg_color, highlightthickness=0,
                selectcolor='light grey', width=30,
                anchor=tkinter.W, text='%s (%s)' % (legend, ip),
                justify=tkinter.LEFT,
                variable=self.selected_ip, value=ip)
            self.ips[ip].grid(row=row, column=2, sticky=sticky)
            if row == 4:
                self.ips[ip].select()
            row += 1
        shift = row

        # Port
        tkinter.Label(self.root,
                      text='Server Port:', bg=bg_color,
                      justify=tkinter.RIGHT).grid(row=shift,
                                                  column=1, pady=10,
                                                  sticky=sticky)

        self.port_number = tkinter.Entry(self.root)
        self.port_number.insert(tkinter.END, options.port)
        self.port_number.grid(row=shift, column=2, sticky=sticky, pady=10)

        # Password
        tkinter.Label(self.root,
                      text='Choose Password:'******'*')
        self.password.bind('<Return>', lambda e: self.start())
        self.password.focus_force()
        self.password.grid(row=shift + 1, column=2, sticky=sticky)

        # Prepare the canvas
        self.canvas = tkinter.Canvas(self.root,
                                     width=400,
                                     height=100,
                                     bg='black')
        self.canvas.grid(row=shift + 2, column=1, columnspan=2, pady=5,
                         sticky=sticky)
        self.canvas.after(1000, self.update_canvas)

        # Prepare the frame
        frame = tkinter.Frame(self.root)
        frame.grid(row=shift + 3, column=1, columnspan=2, pady=5,
                   sticky=sticky)

        # Start button
        self.button_start = tkinter.Button(frame,
                                           text='start server',
                                           command=self.start)

        self.button_start.grid(row=0, column=0, sticky=sticky)

        # Stop button
        self.button_stop = tkinter.Button(frame,
                                          text='stop server',
                                          command=self.stop)

        self.button_stop.grid(row=0, column=1,  sticky=sticky)
        self.button_stop.configure(state='disabled')

        if options.taskbar:
            import gluon.contrib.taskbar_widget
            self.tb = gluon.contrib.taskbar_widget.TaskBarIcon()
            self.checkTaskBar()

            if options.password != '<ask>':
                self.password.insert(0, options.password)
                self.start()
                self.root.withdraw()
        else:
            self.tb = None
Esempio n. 3
0
def console():
    """ Defines the behavior of the console web2py execution """
    import optparse
    import textwrap

    usage = "python web2py.py"

    description = """\
    web2py Web Framework startup script.
    ATTENTION: unless a password is specified (-a 'passwd') web2py will
    attempt to run a GUI. In this case command line options are ignored."""

    description = textwrap.dedent(description)

    parser = optparse.OptionParser(
        usage, None, optparse.Option, ProgramVersion)

    parser.description = description

    msg = ('IP address of the server (e.g., 127.0.0.1 or ::1); '
           'Note: This value is ignored when using the \'interfaces\' option.')
    parser.add_option('-i',
                      '--ip',
                      default='127.0.0.1',
                      dest='ip',
                      help=msg)

    parser.add_option('-p',
                      '--port',
                      default='8000',
                      dest='port',
                      type='int',
                      help='port of server (8000)')

    msg = ('password to be used for administration '
           '(use -a "<recycle>" to reuse the last password))')
    parser.add_option('-a',
                      '--password',
                      default='<ask>',
                      dest='password',
                      help=msg)

    parser.add_option('-c',
                      '--ssl_certificate',
                      default='',
                      dest='ssl_certificate',
                      help='file that contains ssl certificate')

    parser.add_option('-k',
                      '--ssl_private_key',
                      default='',
                      dest='ssl_private_key',
                      help='file that contains ssl private key')

    msg = ('Use this file containing the CA certificate to validate X509 '
           'certificates from clients')
    parser.add_option('--ca-cert',
                      action='store',
                      dest='ssl_ca_certificate',
                      default=None,
                      help=msg)

    parser.add_option('-d',
                      '--pid_filename',
                      default='httpserver.pid',
                      dest='pid_filename',
                      help='file to store the pid of the server')

    parser.add_option('-l',
                      '--log_filename',
                      default='httpserver.log',
                      dest='log_filename',
                      help='file to log connections')

    parser.add_option('-n',
                      '--numthreads',
                      default=None,
                      type='int',
                      dest='numthreads',
                      help='number of threads (deprecated)')

    parser.add_option('--minthreads',
                      default=None,
                      type='int',
                      dest='minthreads',
                      help='minimum number of server threads')

    parser.add_option('--maxthreads',
                      default=None,
                      type='int',
                      dest='maxthreads',
                      help='maximum number of server threads')

    parser.add_option('-s',
                      '--server_name',
                      default=socket.gethostname(),
                      dest='server_name',
                      help='server name for the web server')

    msg = 'max number of queued requests when server unavailable'
    parser.add_option('-q',
                      '--request_queue_size',
                      default='5',
                      type='int',
                      dest='request_queue_size',
                      help=msg)

    parser.add_option('-o',
                      '--timeout',
                      default='10',
                      type='int',
                      dest='timeout',
                      help='timeout for individual request (10 seconds)')

    parser.add_option('-z',
                      '--shutdown_timeout',
                      default='5',
                      type='int',
                      dest='shutdown_timeout',
                      help='timeout on shutdown of server (5 seconds)')

    parser.add_option('--socket-timeout',
                      default=5,
                      type='int',
                      dest='socket_timeout',
                      help='timeout for socket (5 second)')

    parser.add_option('-f',
                      '--folder',
                      default=os.getcwd(),
                      dest='folder',
                      help='folder from which to run web2py')

    parser.add_option('-v',
                      '--verbose',
                      action='store_true',
                      dest='verbose',
                      default=False,
                      help='increase --test verbosity')

    parser.add_option('-Q',
                      '--quiet',
                      action='store_true',
                      dest='quiet',
                      default=False,
                      help='disable all output')

    msg = ('set debug output level (0-100, 0 means all, 100 means none; '
           'default is 30)')
    parser.add_option('-D',
                      '--debug',
                      dest='debuglevel',
                      default=30,
                      type='int',
                      help=msg)

    msg = ('run web2py in interactive shell or IPython (if installed) with '
           'specified appname (if app does not exist it will be created). '
           'APPNAME like a/c/f (c,f optional)')
    parser.add_option('-S',
                      '--shell',
                      dest='shell',
                      metavar='APPNAME',
                      help=msg)

    msg = ('run web2py in interactive shell or bpython (if installed) with '
           'specified appname (if app does not exist it will be created).\n'
           'Use combined with --shell')
    parser.add_option('-B',
                      '--bpython',
                      action='store_true',
                      default=False,
                      dest='bpython',
                      help=msg)

    msg = 'only use plain python shell; should be used with --shell option'
    parser.add_option('-P',
                      '--plain',
                      action='store_true',
                      default=False,
                      dest='plain',
                      help=msg)

    msg = ('auto import model files; default is False; should be used '
           'with --shell option')
    parser.add_option('-M',
                      '--import_models',
                      action='store_true',
                      default=False,
                      dest='import_models',
                      help=msg)

    msg = ('run PYTHON_FILE in web2py environment; '
           'should be used with --shell option')
    parser.add_option('-R',
                      '--run',
                      dest='run',
                      metavar='PYTHON_FILE',
                      default='',
                      help=msg)

    msg = ('run scheduled tasks for the specified apps: expects a list of '
           'app names as -K app1,app2,app3 '
           'or a list of app:groups as -K app1:group1:group2,app2:group1 '
           'to override specific group_names. (only strings, no spaces '
           'allowed. Requires a scheduler defined in the models')
    parser.add_option('-K',
                      '--scheduler',
                      dest='scheduler',
                      default=None,
                      help=msg)

    msg = 'run schedulers alongside webserver, needs -K app1 and -a too'
    parser.add_option('-X',
                      '--with-scheduler',
                      action='store_true',
                      default=False,
                      dest='with_scheduler',
                      help=msg)

    msg = ('run doctests in web2py environment; '
           'TEST_PATH like a/c/f (c,f optional)')
    parser.add_option('-T',
                      '--test',
                      dest='test',
                      metavar='TEST_PATH',
                      default=None,
                      help=msg)

    parser.add_option('-W',
                      '--winservice',
                      dest='winservice',
                      default='',
                      help='-W install|start|stop as Windows service')

    msg = 'trigger a cron run manually; usually invoked from a system crontab'
    parser.add_option('-C',
                      '--cron',
                      action='store_true',
                      dest='extcron',
                      default=False,
                      help=msg)

    msg = 'triggers the use of softcron'
    parser.add_option('--softcron',
                      action='store_true',
                      dest='softcron',
                      default=False,
                      help=msg)

    parser.add_option('-Y',
                      '--run-cron',
                      action='store_true',
                      dest='runcron',
                      default=False,
                      help='start the background cron process')

    parser.add_option('-J',
                      '--cronjob',
                      action='store_true',
                      dest='cronjob',
                      default=False,
                      help='identify cron-initiated command')

    parser.add_option('-L',
                      '--config',
                      dest='config',
                      default='',
                      help='config file')

    parser.add_option('-F',
                      '--profiler',
                      dest='profiler_dir',
                      default=None,
                      help='profiler dir')

    parser.add_option('-t',
                      '--taskbar',
                      action='store_true',
                      dest='taskbar',
                      default=False,
                      help='use web2py gui and run in taskbar (system tray)')

    parser.add_option('',
                      '--nogui',
                      action='store_true',
                      default=False,
                      dest='nogui',
                      help='text-only, no GUI')

    msg = ('should be followed by a list of arguments to be passed to script, '
           'to be used with -S, -A must be the last option')
    parser.add_option('-A',
                      '--args',
                      action='store',
                      dest='args',
                      default=None,
                      help=msg)

    parser.add_option('--no-banner',
                      action='store_true',
                      default=False,
                      dest='nobanner',
                      help='Do not print header banner')

    msg = ('listen on multiple addresses: '
           '"ip1:port1:key1:cert1:ca_cert1;ip2:port2:key2:cert2:ca_cert2;..." '
           '(:key:cert:ca_cert optional; no spaces; IPv6 addresses must be in '
           'square [] brackets)')
    parser.add_option('--interfaces',
                      action='store',
                      dest='interfaces',
                      default=None,
                      help=msg)

    msg = 'runs web2py tests'
    parser.add_option('--run_system_tests',
                      action='store_true',
                      dest='run_system_tests',
                      default=False,
                      help=msg)

    msg = ('adds coverage reporting (needs --run_system_tests), '
           'python 2.7 and the coverage module installed. '
           'You can alter the default path setting the environmental '
           'var "COVERAGE_PROCESS_START". '
           'By default it takes gluon/tests/coverage.ini')
    parser.add_option('--with_coverage',
                      action='store_true',
                      dest='with_coverage',
                      default=False,
                      help=msg)

    if '-A' in sys.argv:
        k = sys.argv.index('-A')
    elif '--args' in sys.argv:
        k = sys.argv.index('--args')
    else:
        k = len(sys.argv)
    sys.argv, other_args = sys.argv[:k], sys.argv[k + 1:]
    (options, args) = parser.parse_args()
    options.args = [options.run] + other_args
    global_settings.cmd_options = options
    global_settings.cmd_args = args

    try:
        options.ips = list(set( # no duplicates
            [addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn())
             if not is_loopback_ip_address(addrinfo=addrinfo)]))
    except socket.gaierror:
        options.ips = []

    if options.run_system_tests:
        run_system_tests(options)

    if options.quiet:
        capture = cStringIO.StringIO()
        sys.stdout = capture
        logger.setLevel(logging.CRITICAL + 1)
    else:
        logger.setLevel(options.debuglevel)

    if options.config[-3:] == '.py':
        options.config = options.config[:-3]

    if options.cronjob:
        global_settings.cronjob = True  # tell the world
        options.plain = True    # cronjobs use a plain shell
        options.nobanner = True
        options.nogui = True

    options.folder = os.path.abspath(options.folder)

    #  accept --interfaces in the form
    #  "ip1:port1:key1:cert1:ca_cert1;[ip2]:port2;ip3:port3:key3:cert3"
    #  (no spaces; optional key:cert indicate SSL)
    if isinstance(options.interfaces, str):
        interfaces = options.interfaces.split(';')
        options.interfaces = []
        for interface in interfaces:
            if interface.startswith('['):  # IPv6
                ip, if_remainder = interface.split(']', 1)
                ip = ip[1:]
                if_remainder = if_remainder[1:].split(':')
                if_remainder[0] = int(if_remainder[0])  # numeric port
                options.interfaces.append(tuple([ip] + if_remainder))
            else:  # IPv4
                interface = interface.split(':')
                interface[1] = int(interface[1])  # numeric port
                options.interfaces.append(tuple(interface))

    #  accepts --scheduler in the form
    #  "app:group1,group2,app2:group1"
    scheduler = []
    options.scheduler_groups = None
    if isinstance(options.scheduler, str):
        if ':' in options.scheduler:
            for opt in options.scheduler.split(','):
                scheduler.append(opt.split(':'))
            options.scheduler = ','.join([app[0] for app in scheduler])
            options.scheduler_groups = scheduler

    if options.numthreads is not None and options.minthreads is None:
        options.minthreads = options.numthreads  # legacy

    create_welcome_w2p()

    if not options.cronjob:
        # If we have the applications package or if we should upgrade
        if not os.path.exists('applications/__init__.py'):
            write_file('applications/__init__.py', '')

    return options, args
Esempio n. 4
0
def console():
    """ Defines the behavior of the console web2py execution """
    import optparse

    parser = optparse.OptionParser(
        usage='python %prog [options]',
        version=ProgramVersion,
        description='web2py Web Framework startup script.',
        epilog='''NOTE: unless a password is specified (-a 'passwd')
web2py will attempt to run a GUI to ask for it
(if not disabled with --nogui).''')

    parser.add_option('-i', '--ip',
                      default='127.0.0.1',
                      help=\
        'IP address of the server (e.g., 127.0.0.1 or ::1); ' \
        'Note: This value is ignored when using the --interfaces option')

    parser.add_option('-p',
                      '--port',
                      default='8000',
                      type='int',
                      help='port of server (%default)')

    parser.add_option('-G', '--GAE', dest='gae',
                      default=None,
                      metavar='APP_NAME', help=\
      "will create app.yaml and gaehandler.py")

    parser.add_option('-a', '--password',
                      default='<ask>',
                      help=\
        'password to be used for administration ' \
        '(use -a "<recycle>" to reuse the last password))')

    parser.add_option('-c',
                      '--ssl_certificate',
                      default='',
                      help='file that contains ssl certificate')

    parser.add_option('-k',
                      '--ssl_private_key',
                      default='',
                      help='file that contains ssl private key')

    parser.add_option('--ca-cert', dest='ssl_ca_certificate',
                      default=None,
                      help=\
        'use this file containing the CA certificate to validate X509 ' \
        'certificates from clients')

    parser.add_option('-d',
                      '--pid_filename',
                      default='httpserver.pid',
                      help='file to store the pid of the server')

    parser.add_option('-l',
                      '--log_filename',
                      default='httpserver.log',
                      help='name for the server log file')

    parser.add_option('-n',
                      '--numthreads',
                      default=None,
                      type='int',
                      help='number of threads (deprecated)')

    parser.add_option('--minthreads',
                      default=None,
                      type='int',
                      help='minimum number of server threads')

    parser.add_option('--maxthreads',
                      default=None,
                      type='int',
                      help='maximum number of server threads')

    parser.add_option('-s',
                      '--server_name',
                      default=socket.gethostname(),
                      help='web server name (%default)')

    parser.add_option('-q', '--request_queue_size',
                      default='5',
                      type='int',
                      help=\
        'max number of queued requests when server unavailable')

    parser.add_option('-o',
                      '--timeout',
                      default='10',
                      type='int',
                      help='timeout for individual request (%default seconds)')

    parser.add_option('-z',
                      '--shutdown_timeout',
                      default='5',
                      type='int',
                      help='timeout on shutdown of server (%default seconds)')

    parser.add_option(
        '--socket-timeout',
        dest='socket_timeout',  # not needed
        default=5,
        type='int',
        help='timeout for socket (%default seconds)')

    parser.add_option('-f',
                      '--folder',
                      default=os.getcwd(),
                      help='folder from which to run web2py')

    parser.add_option('-v',
                      '--verbose',
                      default=False,
                      action='store_true',
                      help='increase --test and --run_system_tests verbosity')

    parser.add_option('-Q',
                      '--quiet',
                      default=False,
                      action='store_true',
                      help='disable all output')

    parser.add_option('-e',
                      '--errors_to_console',
                      dest='print_errors',
                      default=False,
                      action='store_true',
                      help='log all errors to console')

    parser.add_option('-D', '--debug', dest='debuglevel',
                      default=30,
                      type='int',
                      help=\
        'set debug output level (0-100, 0 means all, 100 means none; ' \
        'default is %default)')

    parser.add_option('-S', '--shell',
                      default=None,
                      metavar='APPNAME', help=\
        'run web2py in interactive shell or IPython (if installed) with ' \
        'specified appname (if app does not exist it will be created). ' \
        'APPNAME like a/c/f?x=y (c,f and vars x,y optional)')

    parser.add_option('-B', '--bpython',
                      default=False,
                      action='store_true',
                      help=\
        'run web2py in interactive shell or bpython (if installed) with ' \
        'specified appname (if app does not exist it will be created). ' \
        'Use combined with --shell')

    parser.add_option('-P', '--plain',
                      default=False,
                      action='store_true',
                      help=\
        'only use plain python shell; should be used with --shell option')

    parser.add_option('-M', '--import_models',
                      default=False,
                      action='store_true',
                      help=\
        'auto import model files; default is %default; should be used ' \
        'with --shell option')

    parser.add_option('-R', '--run',
                      default='', # NOTE: used for sys.argv[0] if --shell
                      metavar='PYTHON_FILE', help=\
        'run PYTHON_FILE in web2py environment; ' \
        'should be used with --shell option')

    parser.add_option('-K', '--scheduler',
                      default=None,
                      help=\
        'run scheduled tasks for the specified apps: expects a list of ' \
        'app names as -K app1,app2,app3 ' \
        'or a list of app:groups as -K app1:group1:group2,app2:group1 ' \
        'to override specific group_names. (only strings, no spaces ' \
        'allowed. Requires a scheduler defined in the models')

    parser.add_option('-X', '--with-scheduler', dest='with_scheduler', # not needed
                      default=False,
                      action='store_true',
                      help=\
        'run schedulers alongside webserver, needs -K app1 and -a too')

    parser.add_option('-T', '--test',
                      default=None,
                      metavar='TEST_PATH', help=\
        'run doctests in web2py environment; ' \
        'TEST_PATH like a/c/f (c,f optional)')

    parser.add_option('-C', '--cron', dest='extcron',
                      default=False,
                      action='store_true',
                      help=\
        'trigger a cron run manually; usually invoked from a system crontab')

    parser.add_option('--softcron',
                      default=False,
                      action='store_true',
                      help='triggers the use of softcron')

    parser.add_option('-Y',
                      '--run-cron',
                      dest='runcron',
                      default=False,
                      action='store_true',
                      help='start the background cron process')

    parser.add_option('-J',
                      '--cronjob',
                      default=False,
                      action='store_true',
                      help='identify cron-initiated command')

    parser.add_option('-L', '--config', default='', help='config file')

    parser.add_option('-F',
                      '--profiler',
                      dest='profiler_dir',
                      default=None,
                      help='profiler dir')

    parser.add_option('-t',
                      '--taskbar',
                      default=False,
                      action='store_true',
                      help='use web2py GUI and run in taskbar (system tray)')

    parser.add_option('--nogui',
                      default=False,
                      action='store_true',
                      help='do not run GUI')

    parser.add_option('-A', '--args',
                      default=None,
                      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')

    parser.add_option('--no-banner',
                      dest='nobanner',
                      default=False,
                      action='store_true',
                      help='do not print header banner')

    parser.add_option('--interfaces',
                      default=None,
                      help=\
        'listen on multiple addresses: ' \
        '"ip1:port1:key1:cert1:ca_cert1;ip2:port2:key2:cert2:ca_cert2;..." ' \
        '(:key:cert:ca_cert optional; no spaces; IPv6 addresses must be in ' \
        'square [] brackets)')

    parser.add_option('--run_system_tests',
                      default=False,
                      action='store_true',
                      help='run web2py tests')

    parser.add_option('--with_coverage',
                      default=False,
                      action='store_true',
                      help=\
        'adds coverage reporting (needs --run_system_tests), ' \
        'python 2.7 and the coverage module installed. ' \
        'You can alter the default path setting the environment ' \
        'variable "COVERAGE_PROCESS_START" ' \
        '(by default it takes gluon/tests/coverage.ini)')

    if '-A' in sys.argv:
        k = sys.argv.index('-A')
    elif '--args' in sys.argv:
        k = sys.argv.index('--args')
    else:
        k = len(sys.argv)
    sys.argv, other_args = sys.argv[:k], sys.argv[k + 1:]
    (options, args) = parser.parse_args()
    options.args = other_args

    if options.config.endswith('.py'):
        options.config = options.config[:-3]

    # TODO: process --config here; now is done in start function, too late

    copy_options = copy.deepcopy(options)
    copy_options.password = '******'
    global_settings.cmd_options = copy_options
    global_settings.cmd_args = args

    if options.gae:
        if not os.path.exists('app.yaml'):
            name = options.gae
            # for backward compatibility
            if name == 'configure':
                name = input("Your GAE app name: ")
            content = open(os.path.join('examples', 'app.example.yaml'),
                           'rb').read()
            open('app.yaml', 'wb').write(content.replace("yourappname", name))
        else:
            print("app.yaml alreday exists in the web2py folder")
        if not os.path.exists('gaehandler.py'):
            content = open(os.path.join('handlers', 'gaehandler.py'),
                           'rb').read()
            open('gaehandler.py', 'wb').write(content)
        else:
            print("gaehandler.py alreday exists in the web2py folder")
        sys.exit(0)

    try:
        options.ips = list(
            set(  # no duplicates
                [
                    addrinfo[4][0]
                    for addrinfo in getipaddrinfo(socket.getfqdn())
                    if not is_loopback_ip_address(addrinfo=addrinfo)
                ]))
    except socket.gaierror:
        options.ips = []

    # FIXME: this should be done after create_welcome_w2p
    if options.run_system_tests:
        # run system test and exit
        run_system_tests(options)

    if options.quiet:
        capture = StringIO()
        sys.stdout = capture
        logger.setLevel(logging.CRITICAL + 1)
    else:
        logger.setLevel(options.debuglevel)

    if options.cronjob:
        global_settings.cronjob = True  # tell the world
        options.plain = True  # cronjobs use a plain shell
        options.nobanner = True
        options.nogui = True

    options.folder = os.path.abspath(options.folder)

    #  accept --interfaces in the form
    #  "ip1:port1:key1:cert1:ca_cert1;[ip2]:port2;ip3:port3:key3:cert3"
    #  (no spaces; optional key:cert indicate SSL)
    if isinstance(options.interfaces, str):
        interfaces = options.interfaces.split(';')
        options.interfaces = []
        for interface in interfaces:
            if interface.startswith('['):
                # IPv6
                ip, if_remainder = interface.split(']', 1)
                ip = ip[1:]
                interface = if_remainder[1:].split(':')
                interface.insert(0, ip)
            else:
                # IPv4
                interface = interface.split(':')
            interface[1] = int(interface[1])  # numeric port
            options.interfaces.append(tuple(interface))

    #  accepts --scheduler in the form
    #  "app:group1:group2,app2:group1"
    scheduler = []
    options.scheduler_groups = None
    if isinstance(options.scheduler, str):
        if ':' in options.scheduler:
            for opt in options.scheduler.split(','):
                scheduler.append(opt.split(':'))
            options.scheduler = ','.join([app[0] for app in scheduler])
            options.scheduler_groups = scheduler

    if options.numthreads is not None and options.minthreads is None:
        options.minthreads = options.numthreads  # legacy

    create_welcome_w2p()

    # FIXME: do we still really need this?
    if not options.cronjob:
        # If we have the applications package or if we should upgrade
        if not os.path.exists('applications/__init__.py'):
            write_file('applications/__init__.py', '')

    return options, args
Esempio n. 5
0
def wsgibase(environ, responder):
    """
    The gluon wsgi application. The first function called when a page
    is requested (static or dynamic). It can be called by paste.httpserver
    or by apache mod_wsgi (or any WSGI-compatible server).

      - fills request with info
      - the environment variables, replacing '.' with '_'
      - adds web2py path and version info
      - compensates for fcgi missing path_info and query_string
      - validates the path in url

    The url path must be either:

    1. for static pages:

      - /<application>/static/<file>

    2. for dynamic pages:

      - /<application>[/<controller>[/<function>[/<sub>]]][.<extension>]

    The naming conventions are:

      - application, controller, function and extension may only contain
        `[a-zA-Z0-9_]`
      - file and sub may also contain '-', '=', '.' and '/'
    """
    eget = environ.get
    current.__dict__.clear()
    request = Request(environ)
    response = Response()
    session = Session()
    env = request.env
    #env.web2py_path = global_settings.applications_parent
    env.web2py_version = web2py_version
    #env.update(global_settings)
    static_file = False
    http_response = None
    try:
        try:
            try:
                # ##################################################
                # handle fcgi missing path_info and query_string
                # select rewrite parameters
                # rewrite incoming URL
                # parse rewritten header variables
                # parse rewritten URL
                # serve file if static
                # ##################################################

                fixup_missing_path_info(environ)
                (static_file, version, environ) = url_in(request, environ)
                response.status = env.web2py_status_code or response.status

                if static_file:
                    if eget('QUERY_STRING', '').startswith('attachment'):
                        response.headers['Content-Disposition'] \
                            = 'attachment'
                    if version:
                        response.headers['Cache-Control'] = 'max-age=315360000'
                        response.headers[
                            'Expires'] = 'Thu, 31 Dec 2037 23:59:59 GMT'
                    response.stream(static_file, request=request)

                # ##################################################
                # fill in request items
                # ##################################################
                app = request.application  # must go after url_in!

                if not global_settings.local_hosts:
                    local_hosts = set(['127.0.0.1', '::ffff:127.0.0.1', '::1'])
                    if not global_settings.web2py_runtime_gae:
                        try:
                            fqdn = socket.getfqdn()
                            local_hosts.add(socket.gethostname())
                            local_hosts.add(fqdn)
                            local_hosts.update([
                                addrinfo[4][0]
                                for addrinfo in getipaddrinfo(fqdn)
                            ])
                            if env.server_name:
                                local_hosts.add(env.server_name)
                                local_hosts.update([
                                    addrinfo[4][0] for addrinfo in
                                    getipaddrinfo(env.server_name)
                                ])
                        except (socket.gaierror, TypeError):
                            pass
                    global_settings.local_hosts = list(local_hosts)
                else:
                    local_hosts = global_settings.local_hosts
                client = get_client(env)
                x_req_with = str(env.http_x_requested_with).lower()
                cmd_opts = global_settings.cmd_options

                request.update(
                    client = client,
                    folder = abspath('applications', app) + os.sep,
                    ajax = x_req_with == 'xmlhttprequest',
                    cid = env.http_web2py_component_element,
                    is_local = (env.remote_addr in local_hosts and
                                client == env.remote_addr),
                    is_shell = False,
                    is_scheduler = False,
                    is_https = env.wsgi_url_scheme in HTTPS_SCHEMES or \
                        request.env.http_x_forwarded_proto in HTTPS_SCHEMES \
                        or env.https == 'on'
                    )
                request.url = environ['PATH_INFO']

                # ##################################################
                # access the requested application
                # ##################################################

                disabled = pjoin(request.folder, 'DISABLED')
                if not exists(request.folder):
                    if app == rwthread.routes.default_application \
                            and app != 'welcome':
                        redirect(URL('welcome', 'default', 'index'))
                    elif rwthread.routes.error_handler:
                        _handler = rwthread.routes.error_handler
                        redirect(
                            URL(_handler['application'],
                                _handler['controller'],
                                _handler['function'],
                                args=app))
                    else:
                        raise HTTP(404,
                                   rwthread.routes.error_message %
                                   'invalid request',
                                   web2py_error='invalid application')
                elif not request.is_local and exists(disabled):
                    five0three = os.path.join(request.folder, 'static',
                                              '503.html')
                    if os.path.exists(five0three):
                        raise HTTP(503, file(five0three, 'r').read())
                    else:
                        raise HTTP(
                            503,
                            "<html><body><h1>Temporarily down for maintenance</h1></body></html>"
                        )

                # ##################################################
                # build missing folders
                # ##################################################

                create_missing_app_folders(request)

                # ##################################################
                # get the GET and POST data
                # ##################################################

                #parse_get_post_vars(request, environ)

                # ##################################################
                # expose wsgi hooks for convenience
                # ##################################################

                request.wsgi = LazyWSGI(environ, request, response)

                # ##################################################
                # load cookies
                # ##################################################

                if env.http_cookie:
                    for single_cookie in env.http_cookie.split(';'):
                        single_cookie = single_cookie.strip()
                        if single_cookie:
                            try:
                                request.cookies.load(single_cookie)
                            except Cookie.CookieError:
                                pass  # single invalid cookie ignore

                # ##################################################
                # try load session or create new session file
                # ##################################################

                if not env.web2py_disable_session:
                    session.connect(request, response)

                # ##################################################
                # run controller
                # ##################################################

                if global_settings.debugging and app != "admin":
                    import gluon.debug
                    # activate the debugger
                    gluon.debug.dbg.do_debug(mainpyfile=request.folder)

                serve_controller(request, response, session)
            except HTTP as hr:
                http_response = hr

                if static_file:
                    return http_response.to(responder, env=env)

                if request.body:
                    request.body.close()

                if hasattr(current, 'request'):

                    # ##################################################
                    # on success, try store session in database
                    # ##################################################
                    if not env.web2py_disable_session:
                        session._try_store_in_db(request, response)

                    # ##################################################
                    # on success, commit database
                    # ##################################################

                    if response.do_not_commit is True:
                        BaseAdapter.close_all_instances(None)
                    elif response.custom_commit:
                        BaseAdapter.close_all_instances(response.custom_commit)
                    else:
                        BaseAdapter.close_all_instances('commit')

                    # ##################################################
                    # if session not in db try store session on filesystem
                    # this must be done after trying to commit database!
                    # ##################################################
                    if not env.web2py_disable_session:
                        session._try_store_in_cookie_or_file(request, response)

                    # Set header so client can distinguish component requests.
                    if request.cid:
                        http_response.headers.setdefault(
                            'web2py-component-content', 'replace')

                    if request.ajax:
                        if response.flash:
                            http_response.headers['web2py-component-flash'] = \
                                urllib2.quote(xmlescape(response.flash).replace(b'\n', b''))
                        if response.js:
                            http_response.headers['web2py-component-command'] = \
                                urllib2.quote(response.js.replace('\n', ''))

                    # ##################################################
                    # store cookies in headers
                    # ##################################################

                    session._fixup_before_save()
                    http_response.cookies2headers(response.cookies)

                ticket = None

            except RestrictedError as e:

                if request.body:
                    request.body.close()

                # ##################################################
                # on application error, rollback database
                # ##################################################

                # log tickets before rollback if not in DB
                if not request.tickets_db:
                    ticket = e.log(request) or 'unknown'
                # rollback
                if response._custom_rollback:
                    response._custom_rollback()
                else:
                    BaseAdapter.close_all_instances('rollback')
                # if tickets in db, reconnect and store it in db
                if request.tickets_db:
                    ticket = e.log(request) or 'unknown'

                http_response = \
                    HTTP(500, rwthread.routes.error_message_ticket %
                         dict(ticket=ticket),
                         web2py_error='ticket %s' % ticket)

        except:

            if request.body:
                request.body.close()

            # ##################################################
            # on application error, rollback database
            # ##################################################

            try:
                if response._custom_rollback:
                    response._custom_rollback()
                else:
                    BaseAdapter.close_all_instances('rollback')
            except:
                pass
            e = RestrictedError('Framework', '', '', locals())
            ticket = e.log(request) or 'unrecoverable'
            http_response = \
                HTTP(500, rwthread.routes.error_message_ticket
                     % dict(ticket=ticket),
                     web2py_error='ticket %s' % ticket)

    finally:
        if response and hasattr(response, 'session_file') \
                and response.session_file:
            response.session_file.close()

    session._unlock(response)
    http_response, new_environ = try_rewrite_on_error(http_response, request,
                                                      environ, ticket)
    if not http_response:
        return wsgibase(new_environ, responder)
    if global_settings.web2py_crontype == 'soft':
        newcron.softcron(global_settings.applications_parent).start()
    return http_response.to(responder, env=env)
Esempio n. 6
0
def console():
    """ Defines the behavior of the console web2py execution """
    import optparse
    import textwrap

    usage = "python web2py.py"

    description = """\
    web2py Web Framework startup script.
    ATTENTION: unless a password is specified (-a 'passwd') web2py will
    attempt to run a GUI. In this case command line options are ignored."""

    description = textwrap.dedent(description)

    parser = optparse.OptionParser(usage, None, optparse.Option,
                                   ProgramVersion)

    parser.description = description

    msg = ('IP address of the server (e.g., 127.0.0.1 or ::1); '
           'Note: This value is ignored when using the \'interfaces\' option.')
    parser.add_option('-i', '--ip', default='127.0.0.1', dest='ip', help=msg)

    parser.add_option('-p',
                      '--port',
                      default='8000',
                      dest='port',
                      type='int',
                      help='port of server (8000)')

    msg = ('password to be used for administration '
           '(use -a "<recycle>" to reuse the last password))')
    parser.add_option('-a',
                      '--password',
                      default='<ask>',
                      dest='password',
                      help=msg)

    parser.add_option('-c',
                      '--ssl_certificate',
                      default='',
                      dest='ssl_certificate',
                      help='file that contains ssl certificate')

    parser.add_option('-k',
                      '--ssl_private_key',
                      default='',
                      dest='ssl_private_key',
                      help='file that contains ssl private key')

    msg = ('Use this file containing the CA certificate to validate X509 '
           'certificates from clients')
    parser.add_option('--ca-cert',
                      action='store',
                      dest='ssl_ca_certificate',
                      default=None,
                      help=msg)

    parser.add_option('-d',
                      '--pid_filename',
                      default='httpserver.pid',
                      dest='pid_filename',
                      help='file to store the pid of the server')

    parser.add_option('-l',
                      '--log_filename',
                      default='httpserver.log',
                      dest='log_filename',
                      help='file to log connections')

    parser.add_option('-n',
                      '--numthreads',
                      default=None,
                      type='int',
                      dest='numthreads',
                      help='number of threads (deprecated)')

    parser.add_option('--minthreads',
                      default=None,
                      type='int',
                      dest='minthreads',
                      help='minimum number of server threads')

    parser.add_option('--maxthreads',
                      default=None,
                      type='int',
                      dest='maxthreads',
                      help='maximum number of server threads')

    parser.add_option('-s',
                      '--server_name',
                      default=socket.gethostname(),
                      dest='server_name',
                      help='server name for the web server')

    msg = 'max number of queued requests when server unavailable'
    parser.add_option('-q',
                      '--request_queue_size',
                      default='5',
                      type='int',
                      dest='request_queue_size',
                      help=msg)

    parser.add_option('-o',
                      '--timeout',
                      default='10',
                      type='int',
                      dest='timeout',
                      help='timeout for individual request (10 seconds)')

    parser.add_option('-z',
                      '--shutdown_timeout',
                      default='5',
                      type='int',
                      dest='shutdown_timeout',
                      help='timeout on shutdown of server (5 seconds)')

    parser.add_option('--socket-timeout',
                      default=5,
                      type='int',
                      dest='socket_timeout',
                      help='timeout for socket (5 second)')

    parser.add_option('-f',
                      '--folder',
                      default=os.getcwd(),
                      dest='folder',
                      help='folder from which to run web2py')

    parser.add_option('-v',
                      '--verbose',
                      action='store_true',
                      dest='verbose',
                      default=False,
                      help='increase --test verbosity')

    parser.add_option('-Q',
                      '--quiet',
                      action='store_true',
                      dest='quiet',
                      default=False,
                      help='disable all output')

    msg = ('set debug output level (0-100, 0 means all, 100 means none; '
           'default is 30)')
    parser.add_option('-D',
                      '--debug',
                      dest='debuglevel',
                      default=30,
                      type='int',
                      help=msg)

    msg = ('run web2py in interactive shell or IPython (if installed) with '
           'specified appname (if app does not exist it will be created). '
           'APPNAME like a/c/f (c,f optional)')
    parser.add_option('-S',
                      '--shell',
                      dest='shell',
                      metavar='APPNAME',
                      help=msg)

    msg = ('run web2py in interactive shell or bpython (if installed) with '
           'specified appname (if app does not exist it will be created).\n'
           'Use combined with --shell')
    parser.add_option('-B',
                      '--bpython',
                      action='store_true',
                      default=False,
                      dest='bpython',
                      help=msg)

    msg = 'only use plain python shell; should be used with --shell option'
    parser.add_option('-P',
                      '--plain',
                      action='store_true',
                      default=False,
                      dest='plain',
                      help=msg)

    msg = ('auto import model files; default is False; should be used '
           'with --shell option')
    parser.add_option('-M',
                      '--import_models',
                      action='store_true',
                      default=False,
                      dest='import_models',
                      help=msg)

    msg = ('run PYTHON_FILE in web2py environment; '
           'should be used with --shell option')
    parser.add_option('-R',
                      '--run',
                      dest='run',
                      metavar='PYTHON_FILE',
                      default='',
                      help=msg)

    msg = ('run scheduled tasks for the specified apps: expects a list of '
           'app names as -K app1,app2,app3 '
           'or a list of app:groups as -K app1:group1:group2,app2:group1 '
           'to override specific group_names. (only strings, no spaces '
           'allowed. Requires a scheduler defined in the models')
    parser.add_option('-K',
                      '--scheduler',
                      dest='scheduler',
                      default=None,
                      help=msg)

    msg = 'run schedulers alongside webserver, needs -K app1 and -a too'
    parser.add_option('-X',
                      '--with-scheduler',
                      action='store_true',
                      default=False,
                      dest='with_scheduler',
                      help=msg)

    msg = ('run doctests in web2py environment; '
           'TEST_PATH like a/c/f (c,f optional)')
    parser.add_option('-T',
                      '--test',
                      dest='test',
                      metavar='TEST_PATH',
                      default=None,
                      help=msg)

    msg = 'trigger a cron run manually; usually invoked from a system crontab'
    parser.add_option('-C',
                      '--cron',
                      action='store_true',
                      dest='extcron',
                      default=False,
                      help=msg)

    msg = 'triggers the use of softcron'
    parser.add_option('--softcron',
                      action='store_true',
                      dest='softcron',
                      default=False,
                      help=msg)

    parser.add_option('-Y',
                      '--run-cron',
                      action='store_true',
                      dest='runcron',
                      default=False,
                      help='start the background cron process')

    parser.add_option('-J',
                      '--cronjob',
                      action='store_true',
                      dest='cronjob',
                      default=False,
                      help='identify cron-initiated command')

    parser.add_option('-L',
                      '--config',
                      dest='config',
                      default='',
                      help='config file')

    parser.add_option('-F',
                      '--profiler',
                      dest='profiler_dir',
                      default=None,
                      help='profiler dir')

    parser.add_option('-t',
                      '--taskbar',
                      action='store_true',
                      dest='taskbar',
                      default=False,
                      help='use web2py gui and run in taskbar (system tray)')

    parser.add_option('',
                      '--nogui',
                      action='store_true',
                      default=False,
                      dest='nogui',
                      help='text-only, no GUI')

    msg = ('should be followed by a list of arguments to be passed to script, '
           'to be used with -S, -A must be the last option')
    parser.add_option('-A',
                      '--args',
                      action='store',
                      dest='args',
                      default=None,
                      help=msg)

    parser.add_option('--no-banner',
                      action='store_true',
                      default=False,
                      dest='nobanner',
                      help='Do not print header banner')

    msg = ('listen on multiple addresses: '
           '"ip1:port1:key1:cert1:ca_cert1;ip2:port2:key2:cert2:ca_cert2;..." '
           '(:key:cert:ca_cert optional; no spaces; IPv6 addresses must be in '
           'square [] brackets)')
    parser.add_option('--interfaces',
                      action='store',
                      dest='interfaces',
                      default=None,
                      help=msg)

    msg = 'runs web2py tests'
    parser.add_option('--run_system_tests',
                      action='store_true',
                      dest='run_system_tests',
                      default=False,
                      help=msg)

    msg = ('adds coverage reporting (needs --run_system_tests), '
           'python 2.7 and the coverage module installed. '
           'You can alter the default path setting the environmental '
           'var "COVERAGE_PROCESS_START". '
           'By default it takes gluon/tests/coverage.ini')
    parser.add_option('--with_coverage',
                      action='store_true',
                      dest='with_coverage',
                      default=False,
                      help=msg)

    if '-A' in sys.argv:
        k = sys.argv.index('-A')
    elif '--args' in sys.argv:
        k = sys.argv.index('--args')
    else:
        k = len(sys.argv)
    sys.argv, other_args = sys.argv[:k], sys.argv[k + 1:]
    (options, args) = parser.parse_args()
    options.args = [options.run] + other_args
    global_settings.cmd_options = options
    global_settings.cmd_args = args

    try:
        options.ips = list(
            set(  # no duplicates
                [
                    addrinfo[4][0]
                    for addrinfo in getipaddrinfo(socket.getfqdn())
                    if not is_loopback_ip_address(addrinfo=addrinfo)
                ]))
    except socket.gaierror:
        options.ips = []

    if options.run_system_tests:
        run_system_tests(options)

    if options.quiet:
        capture = cStringIO.StringIO()
        sys.stdout = capture
        logger.setLevel(logging.CRITICAL + 1)
    else:
        logger.setLevel(options.debuglevel)

    if options.config[-3:] == '.py':
        options.config = options.config[:-3]

    if options.cronjob:
        global_settings.cronjob = True  # tell the world
        options.plain = True  # cronjobs use a plain shell
        options.nobanner = True
        options.nogui = True

    options.folder = os.path.abspath(options.folder)

    #  accept --interfaces in the form
    #  "ip1:port1:key1:cert1:ca_cert1;[ip2]:port2;ip3:port3:key3:cert3"
    #  (no spaces; optional key:cert indicate SSL)
    if isinstance(options.interfaces, str):
        interfaces = options.interfaces.split(';')
        options.interfaces = []
        for interface in interfaces:
            if interface.startswith('['):  # IPv6
                ip, if_remainder = interface.split(']', 1)
                ip = ip[1:]
                if_remainder = if_remainder[1:].split(':')
                if_remainder[0] = int(if_remainder[0])  # numeric port
                options.interfaces.append(tuple([ip] + if_remainder))
            else:  # IPv4
                interface = interface.split(':')
                interface[1] = int(interface[1])  # numeric port
                options.interfaces.append(tuple(interface))

    #  accepts --scheduler in the form
    #  "app:group1,group2,app2:group1"
    scheduler = []
    options.scheduler_groups = None
    if isinstance(options.scheduler, str):
        if ':' in options.scheduler:
            for opt in options.scheduler.split(','):
                scheduler.append(opt.split(':'))
            options.scheduler = ','.join([app[0] for app in scheduler])
            options.scheduler_groups = scheduler

    if options.numthreads is not None and options.minthreads is None:
        options.minthreads = options.numthreads  # legacy

    create_welcome_w2p()

    if not options.cronjob:
        # If we have the applications package or if we should upgrade
        if not os.path.exists('applications/__init__.py'):
            write_file('applications/__init__.py', '')

    return options, args
Esempio n. 7
0
    def __init__(self, root, options):
        """ web2pyDialog constructor  """

        if PY2:
            import Tkinter as tkinter
            import tkMessageBox as messagebox
        else:
            import tkinter
            from tkinter import messagebox


        bg_color = 'white'
        root.withdraw()

        self.root = tkinter.Toplevel(root, bg=bg_color)
        self.root.resizable(0, 0)
        self.root.title(ProgramName)

        self.options = options
        self.scheduler_processes = {}
        self.menu = tkinter.Menu(self.root)
        servermenu = tkinter.Menu(self.menu, tearoff=0)
        httplog = os.path.join(self.options.folder, self.options.log_filename)
        iconphoto = os.path.join('extras', 'icons', 'web2py.gif')
        if os.path.exists(iconphoto):
            img = tkinter.PhotoImage(file=iconphoto)
            self.root.tk.call('wm', 'iconphoto', self.root._w, img)
        # Building the Menu
        item = lambda: start_browser(httplog)
        servermenu.add_command(label='View httpserver.log',
                               command=item)

        servermenu.add_command(label='Quit (pid:%i)' % os.getpid(),
                               command=self.quit)

        self.menu.add_cascade(label='Server', menu=servermenu)

        self.pagesmenu = tkinter.Menu(self.menu, tearoff=0)
        self.menu.add_cascade(label='Pages', menu=self.pagesmenu)

        #scheduler menu
        self.schedmenu = tkinter.Menu(self.menu, tearoff=0)
        self.menu.add_cascade(label='Scheduler', menu=self.schedmenu)
        #start and register schedulers from options
        self.update_schedulers(start=True)

        helpmenu = tkinter.Menu(self.menu, tearoff=0)

        # Home Page
        item = lambda: start_browser('http://www.web2py.com/')
        helpmenu.add_command(label='Home Page',
                             command=item)

        # About
        ProgramInfo = """%s
                 %s
                 %s""" % (ProgramName, ProgramAuthor, ProgramVersion)
        item = lambda: messagebox.showinfo('About web2py', ProgramInfo)
        helpmenu.add_command(label='About',
                             command=item)

        self.menu.add_cascade(label='Info', menu=helpmenu)

        self.root.config(menu=self.menu)

        if options.taskbar:
            self.root.protocol('WM_DELETE_WINDOW',
                               lambda: self.quit(True))
        else:
            self.root.protocol('WM_DELETE_WINDOW', self.quit)

        sticky = tkinter.NW

        # Prepare the logo area
        self.logoarea = tkinter.Canvas(self.root,
                                background=bg_color,
                                width=300,
                                height=300)
        self.logoarea.grid(row=0, column=0, columnspan=4, sticky=sticky)
        self.logoarea.after(1000, self.update_canvas)

        logo = os.path.join('extras', 'icons', 'splashlogo.gif')
        if os.path.exists(logo):
            img = tkinter.PhotoImage(file=logo)
            pnl = tkinter.Label(self.logoarea, image=img, background=bg_color, bd=0)
            pnl.pack(side='top', fill='both', expand='yes')
            # Prevent garbage collection of img
            pnl.image = img

        # Prepare the banner area
        self.bannerarea = tkinter.Canvas(self.root,
                                bg=bg_color,
                                width=300,
                                height=300)
        self.bannerarea.grid(row=1, column=1, columnspan=2, sticky=sticky)

        tkinter.Label(self.bannerarea, anchor=tkinter.N,
                      text=str(ProgramVersion + "\n" + ProgramAuthor),
                      font=('Helvetica', 11), justify=tkinter.CENTER,
                      foreground='#195866', background=bg_color,
                      height=3).pack(side='top',
                                     fill='both',
                                     expand='yes')

        self.bannerarea.after(1000, self.update_canvas)

        # IP
        # retrieves the list of server IP addresses
        try:
            if_ips = list(set(  # no duplicates
                [addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn())
                 if not is_loopback_ip_address(addrinfo=addrinfo)]))
        except socket.gaierror:
            if_ips = []

        tkinter.Label(self.root,
                      text='Server IP:', bg=bg_color,
                      justify=tkinter.RIGHT).grid(row=4,
                                                  column=1,
                                                  sticky=sticky)
        self.ips = {}
        self.selected_ip = tkinter.StringVar()
        row = 4
        ips = [('127.0.0.1', 'Local (IPv4)')] + \
            ([('::1', 'Local (IPv6)')] if socket.has_ipv6 else []) + \
            [(ip, 'Public') for ip in if_ips] + \
            [('0.0.0.0', 'Public')]
        for ip, legend in ips:
            self.ips[ip] = tkinter.Radiobutton(
                self.root, bg=bg_color, highlightthickness=0,
                selectcolor='light grey', width=30,
                anchor=tkinter.W, text='%s (%s)' % (legend, ip),
                justify=tkinter.LEFT,
                variable=self.selected_ip, value=ip)
            self.ips[ip].grid(row=row, column=2, sticky=sticky)
            if row == 4:
                self.ips[ip].select()
            row += 1
        shift = row

        # Port
        tkinter.Label(self.root,
                      text='Server Port:', bg=bg_color,
                      justify=tkinter.RIGHT).grid(row=shift,
                                                  column=1, pady=10,
                                                  sticky=sticky)

        self.port_number = tkinter.Entry(self.root)
        self.port_number.insert(tkinter.END, self.options.port)
        self.port_number.grid(row=shift, column=2, sticky=sticky, pady=10)

        # Password
        tkinter.Label(self.root,
                      text='Choose Password:'******'*')
        self.password.bind('<Return>', lambda e: self.start())
        self.password.focus_force()
        self.password.grid(row=shift + 1, column=2, sticky=sticky)

        # Prepare the canvas
        self.canvas = tkinter.Canvas(self.root,
                                     width=400,
                                     height=100,
                                     bg='black')
        self.canvas.grid(row=shift + 2, column=1, columnspan=2, pady=5,
                         sticky=sticky)
        self.canvas.after(1000, self.update_canvas)

        # Prepare the frame
        frame = tkinter.Frame(self.root)
        frame.grid(row=shift + 3, column=1, columnspan=2, pady=5,
                   sticky=sticky)

        # Start button
        self.button_start = tkinter.Button(frame,
                                           text='start server',
                                           command=self.start)

        self.button_start.grid(row=0, column=0, sticky=sticky)

        # Stop button
        self.button_stop = tkinter.Button(frame,
                                          text='stop server',
                                          command=self.stop)

        self.button_stop.grid(row=0, column=1,  sticky=sticky)
        self.button_stop.configure(state='disabled')

        if options.taskbar:
            import gluon.contrib.taskbar_widget
            self.tb = gluon.contrib.taskbar_widget.TaskBarIcon()
            self.checkTaskBar()

            if options.password != '<ask>':
                self.password.insert(0, options.password)
                self.start()
                self.root.withdraw()
        else:
            self.tb = None
Esempio n. 8
0
def console():
    """ Defines the behavior of the console web2py execution """
    import optparse

    parser = optparse.OptionParser(
        usage='python %prog [options]',
        version=ProgramVersion,
        description='web2py Web Framework startup script.',
        epilog='''NOTE: unless a password is specified (-a 'passwd')
web2py will attempt to run a GUI to ask for it
(if not disabled with --nogui).''')

    parser.add_option('-i', '--ip',
                      default='127.0.0.1',
                      help=\
        'IP address of the server (e.g., 127.0.0.1 or ::1); ' \
        'Note: This value is ignored when using the --interfaces option')

    parser.add_option('-p', '--port',
                      default='8000',
                      type='int',
                      help='port of server (%default)')

    parser.add_option('-G', '--GAE', dest='gae',
                      default=None,
                      metavar='APP_NAME', help=\
      "will create app.yaml and gaehandler.py")

    parser.add_option('-a', '--password',
                      default='<ask>',
                      help=\
        'password to be used for administration ' \
        '(use -a "<recycle>" to reuse the last password))')

    parser.add_option('-c', '--ssl_certificate',
                      default='',
                      help='file that contains ssl certificate')

    parser.add_option('-k', '--ssl_private_key',
                      default='',
                      help='file that contains ssl private key')

    parser.add_option('--ca-cert', dest='ssl_ca_certificate',
                      default=None,
                      help=\
        'use this file containing the CA certificate to validate X509 ' \
        'certificates from clients')

    parser.add_option('-d', '--pid_filename',
                      default='httpserver.pid',
                      help='file to store the pid of the server')

    parser.add_option('-l', '--log_filename',
                      default='httpserver.log',
                      help='name for the server log file')

    parser.add_option('-n', '--numthreads',
                      default=None,
                      type='int',
                      help='number of threads (deprecated)')

    parser.add_option('--minthreads',
                      default=None,
                      type='int',
                      help='minimum number of server threads')

    parser.add_option('--maxthreads',
                      default=None,
                      type='int',
                      help='maximum number of server threads')

    parser.add_option('-s', '--server_name',
                      default=socket.gethostname(),
                      help='web server name (%default)')

    parser.add_option('-q', '--request_queue_size',
                      default='5',
                      type='int',
                      help=\
        'max number of queued requests when server unavailable')

    parser.add_option('-o', '--timeout',
                      default='10',
                      type='int',
                      help='timeout for individual request (%default seconds)')

    parser.add_option('-z', '--shutdown_timeout',
                      default='5',
                      type='int',
                      help='timeout on shutdown of server (%default seconds)')

    parser.add_option('--socket-timeout', dest='socket_timeout', # not needed
                      default=5,
                      type='int',
                      help='timeout for socket (%default seconds)')

    parser.add_option('-f', '--folder',
                      default=os.getcwd(),
                      help='folder from which to run web2py')

    parser.add_option('-v', '--verbose',
                      default=False,
                      action='store_true',
                      help='increase --test and --run_system_tests verbosity')

    parser.add_option('-Q', '--quiet',
                      default=False,
                      action='store_true',
                      help='disable all output')

    parser.add_option('-e', '--errors_to_console', dest='print_errors',
                      default=False,
                      action='store_true',
                      help='log all errors to console')

    parser.add_option('-D', '--debug', dest='debuglevel',
                      default=30,
                      type='int',
                      help=\
        'set debug output level (0-100, 0 means all, 100 means none; ' \
        'default is %default)')

    parser.add_option('-S', '--shell',
                      default=None,
                      metavar='APPNAME', help=\
        'run web2py in interactive shell or IPython (if installed) with ' \
        'specified appname (if app does not exist it will be created). ' \
        'APPNAME like a/c/f?x=y (c,f and vars x,y optional)')

    parser.add_option('-B', '--bpython',
                      default=False,
                      action='store_true',
                      help=\
        'run web2py in interactive shell or bpython (if installed) with ' \
        'specified appname (if app does not exist it will be created). ' \
        'Use combined with --shell')

    parser.add_option('-P', '--plain',
                      default=False,
                      action='store_true',
                      help=\
        'only use plain python shell; should be used with --shell option')

    parser.add_option('-M', '--import_models',
                      default=False,
                      action='store_true',
                      help=\
        'auto import model files; default is %default; should be used ' \
        'with --shell option')

    parser.add_option('-R', '--run',
                      default='', # NOTE: used for sys.argv[0] if --shell
                      metavar='PYTHON_FILE', help=\
        'run PYTHON_FILE in web2py environment; ' \
        'should be used with --shell option')

    parser.add_option('-K', '--scheduler',
                      default=None,
                      help=\
        'run scheduled tasks for the specified apps: expects a list of ' \
        'app names as -K app1,app2,app3 ' \
        'or a list of app:groups as -K app1:group1:group2,app2:group1 ' \
        'to override specific group_names. (only strings, no spaces ' \
        'allowed. Requires a scheduler defined in the models')

    parser.add_option('-X', '--with-scheduler', dest='with_scheduler', # not needed
                      default=False,
                      action='store_true',
                      help=\
        'run schedulers alongside webserver, needs -K app1 and -a too')

    parser.add_option('-T', '--test',
                      default=None,
                      metavar='TEST_PATH', help=\
        'run doctests in web2py environment; ' \
        'TEST_PATH like a/c/f (c,f optional)')

    parser.add_option('-C', '--cron', dest='extcron',
                      default=False,
                      action='store_true',
                      help=\
        'trigger a cron run manually; usually invoked from a system crontab')

    parser.add_option('--softcron',
                      default=False,
                      action='store_true',
                      help='triggers the use of softcron')

    parser.add_option('-Y', '--run-cron', dest='runcron',
                      default=False,
                      action='store_true',
                      help='start the background cron process')

    parser.add_option('-J', '--cronjob',
                      default=False,
                      action='store_true',
                      help='identify cron-initiated command')

    parser.add_option('-L', '--config',
                      default='',
                      help='config file')

    parser.add_option('-F', '--profiler', dest='profiler_dir',
                      default=None,
                      help='profiler dir')

    parser.add_option('-t', '--taskbar',
                      default=False,
                      action='store_true',
                      help='use web2py GUI and run in taskbar (system tray)')

    parser.add_option('--nogui',
                      default=False,
                      action='store_true',
                      help='do not run GUI')

    parser.add_option('-A', '--args',
                      default=None,
                      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')

    parser.add_option('--no-banner', dest='nobanner',
                      default=False,
                      action='store_true',
                      help='do not print header banner')

    parser.add_option('--interfaces',
                      default=None,
                      help=\
        'listen on multiple addresses: ' \
        '"ip1:port1:key1:cert1:ca_cert1;ip2:port2:key2:cert2:ca_cert2;..." ' \
        '(:key:cert:ca_cert optional; no spaces; IPv6 addresses must be in ' \
        'square [] brackets)')

    parser.add_option('--run_system_tests',
                      default=False,
                      action='store_true',
                      help='run web2py tests')

    parser.add_option('--with_coverage',
                      default=False,
                      action='store_true',
                      help=\
        'adds coverage reporting (needs --run_system_tests), ' \
        'python 2.7 and the coverage module installed. ' \
        'You can alter the default path setting the environment ' \
        'variable "COVERAGE_PROCESS_START" ' \
        '(by default it takes gluon/tests/coverage.ini)')

    if '-A' in sys.argv:
        k = sys.argv.index('-A')
    elif '--args' in sys.argv:
        k = sys.argv.index('--args')
    else:
        k = len(sys.argv)
    sys.argv, other_args = sys.argv[:k], sys.argv[k + 1:]
    (options, args) = parser.parse_args()
    options.args = other_args

    if options.config.endswith('.py'):
        options.config = options.config[:-3]

    # TODO: process --config here; now is done in start function, too late

    copy_options = copy.deepcopy(options)
    copy_options.password = '******'
    global_settings.cmd_options = copy_options
    global_settings.cmd_args = args

    if options.gae:
        if not os.path.exists('app.yaml'):
            name = options.gae
            # for backward compatibility
            if name == 'configure':
                name = input("Your GAE app name: ")
            content = open(os.path.join('examples', 'app.example.yaml'), 'rb').read()
            open('app.yaml', 'wb').write(content.replace("yourappname", name))
        else:
            print("app.yaml alreday exists in the web2py folder")
        if not os.path.exists('gaehandler.py'):
            content = open(os.path.join('handlers', 'gaehandler.py'), 'rb').read()
            open('gaehandler.py', 'wb').write(content)
        else:
            print("gaehandler.py alreday exists in the web2py folder")
        sys.exit(0)

    try:
        options.ips = list(set(  # no duplicates
            [addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn())
             if not is_loopback_ip_address(addrinfo=addrinfo)]))
    except socket.gaierror:
        options.ips = []

    # FIXME: this should be done after create_welcome_w2p
    if options.run_system_tests:
        # run system test and exit
        run_system_tests(options)

    if options.quiet:
        capture = StringIO()
        sys.stdout = capture
        logger.setLevel(logging.CRITICAL + 1)
    else:
        logger.setLevel(options.debuglevel)

    if options.cronjob:
        global_settings.cronjob = True  # tell the world
        options.plain = True    # cronjobs use a plain shell
        options.nobanner = True
        options.nogui = True

    options.folder = os.path.abspath(options.folder)

    #  accept --interfaces in the form
    #  "ip1:port1:key1:cert1:ca_cert1;[ip2]:port2;ip3:port3:key3:cert3"
    #  (no spaces; optional key:cert indicate SSL)
    if isinstance(options.interfaces, str):
        interfaces = options.interfaces.split(';')
        options.interfaces = []
        for interface in interfaces:
            if interface.startswith('['):
                # IPv6
                ip, if_remainder = interface.split(']', 1)
                ip = ip[1:]
                interface = if_remainder[1:].split(':')
                interface.insert(0, ip)
            else:
                # IPv4
                interface = interface.split(':')
            interface[1] = int(interface[1])  # numeric port
            options.interfaces.append(tuple(interface))

    #  accepts --scheduler in the form
    #  "app:group1:group2,app2:group1"
    scheduler = []
    options.scheduler_groups = None
    if isinstance(options.scheduler, str):
        if ':' in options.scheduler:
            for opt in options.scheduler.split(','):
                scheduler.append(opt.split(':'))
            options.scheduler = ','.join([app[0] for app in scheduler])
            options.scheduler_groups = scheduler

    if options.numthreads is not None and options.minthreads is None:
        options.minthreads = options.numthreads  # legacy

    create_welcome_w2p()

    # FIXME: do we still really need this?
    if not options.cronjob:
        # If we have the applications package or if we should upgrade
        if not os.path.exists('applications/__init__.py'):
            write_file('applications/__init__.py', '')

    return options, args
Esempio n. 9
0
def console():
    """ Defines the behavior of the console web2py execution """
    import optparse
    import textwrap

    usage = "python web2py.py"

    description = """\
    web2py Web Framework startup script.
    ATTENTION: unless a password is specified (-a 'passwd') web2py will
    attempt to run a GUI. In this case command line options are ignored."""

    description = textwrap.dedent(description)

    parser = optparse.OptionParser(usage, None, optparse.Option, ProgramVersion)

    parser.description = description

    msg = (
        "IP address of the server (e.g., 127.0.0.1 or ::1); "
        "Note: This value is ignored when using the 'interfaces' option."
    )
    parser.add_option("-i", "--ip", default="127.0.0.1", dest="ip", help=msg)

    parser.add_option("-p", "--port", default="8000", dest="port", type="int", help="port of server (8000)")

    parser.add_option(
        "-G", "--GAE", default=None, dest="gae", help="'-G configure' will create app.yaml and gaehandler.py"
    )

    msg = "password to be used for administration " '(use -a "<recycle>" to reuse the last password))'
    parser.add_option("-a", "--password", default="<ask>", dest="password", help=msg)

    parser.add_option(
        "-c", "--ssl_certificate", default="", dest="ssl_certificate", help="file that contains ssl certificate"
    )

    parser.add_option(
        "-k", "--ssl_private_key", default="", dest="ssl_private_key", help="file that contains ssl private key"
    )

    msg = "Use this file containing the CA certificate to validate X509 " "certificates from clients"
    parser.add_option("--ca-cert", action="store", dest="ssl_ca_certificate", default=None, help=msg)

    parser.add_option(
        "-d",
        "--pid_filename",
        default="httpserver.pid",
        dest="pid_filename",
        help="file to store the pid of the server",
    )

    parser.add_option(
        "-l", "--log_filename", default="httpserver.log", dest="log_filename", help="file to log connections"
    )

    parser.add_option(
        "-n", "--numthreads", default=None, type="int", dest="numthreads", help="number of threads (deprecated)"
    )

    parser.add_option(
        "--minthreads", default=None, type="int", dest="minthreads", help="minimum number of server threads"
    )

    parser.add_option(
        "--maxthreads", default=None, type="int", dest="maxthreads", help="maximum number of server threads"
    )

    parser.add_option(
        "-s", "--server_name", default=socket.gethostname(), dest="server_name", help="server name for the web server"
    )

    msg = "max number of queued requests when server unavailable"
    parser.add_option("-q", "--request_queue_size", default="5", type="int", dest="request_queue_size", help=msg)

    parser.add_option(
        "-o", "--timeout", default="10", type="int", dest="timeout", help="timeout for individual request (10 seconds)"
    )

    parser.add_option(
        "-z",
        "--shutdown_timeout",
        default="5",
        type="int",
        dest="shutdown_timeout",
        help="timeout on shutdown of server (5 seconds)",
    )

    parser.add_option(
        "--socket-timeout", default=5, type="int", dest="socket_timeout", help="timeout for socket (5 second)"
    )

    parser.add_option("-f", "--folder", default=os.getcwd(), dest="folder", help="folder from which to run web2py")

    parser.add_option(
        "-v", "--verbose", action="store_true", dest="verbose", default=False, help="increase --test verbosity"
    )

    parser.add_option("-Q", "--quiet", action="store_true", dest="quiet", default=False, help="disable all output")

    parser.add_option(
        "-e",
        "--errors_to_console",
        action="store_true",
        dest="print_errors",
        default=False,
        help="log all errors to console",
    )

    msg = "set debug output level (0-100, 0 means all, 100 means none; " "default is 30)"
    parser.add_option("-D", "--debug", dest="debuglevel", default=30, type="int", help=msg)

    msg = (
        "run web2py in interactive shell or IPython (if installed) with "
        "specified appname (if app does not exist it will be created). "
        "APPNAME like a/c/f (c,f optional)"
    )
    parser.add_option("-S", "--shell", dest="shell", metavar="APPNAME", help=msg)

    msg = (
        "run web2py in interactive shell or bpython (if installed) with "
        "specified appname (if app does not exist it will be created).\n"
        "Use combined with --shell"
    )
    parser.add_option("-B", "--bpython", action="store_true", default=False, dest="bpython", help=msg)

    msg = "only use plain python shell; should be used with --shell option"
    parser.add_option("-P", "--plain", action="store_true", default=False, dest="plain", help=msg)

    msg = "auto import model files; default is False; should be used " "with --shell option"
    parser.add_option("-M", "--import_models", action="store_true", default=False, dest="import_models", help=msg)

    msg = "run PYTHON_FILE in web2py environment; " "should be used with --shell option"
    parser.add_option("-R", "--run", dest="run", metavar="PYTHON_FILE", default="", help=msg)

    msg = (
        "run scheduled tasks for the specified apps: expects a list of "
        "app names as -K app1,app2,app3 "
        "or a list of app:groups as -K app1:group1:group2,app2:group1 "
        "to override specific group_names. (only strings, no spaces "
        "allowed. Requires a scheduler defined in the models"
    )
    parser.add_option("-K", "--scheduler", dest="scheduler", default=None, help=msg)

    msg = "run schedulers alongside webserver, needs -K app1 and -a too"
    parser.add_option("-X", "--with-scheduler", action="store_true", default=False, dest="with_scheduler", help=msg)

    msg = "run doctests in web2py environment; " "TEST_PATH like a/c/f (c,f optional)"
    parser.add_option("-T", "--test", dest="test", metavar="TEST_PATH", default=None, help=msg)

    msg = "trigger a cron run manually; usually invoked from a system crontab"
    parser.add_option("-C", "--cron", action="store_true", dest="extcron", default=False, help=msg)

    msg = "triggers the use of softcron"
    parser.add_option("--softcron", action="store_true", dest="softcron", default=False, help=msg)

    parser.add_option(
        "-Y", "--run-cron", action="store_true", dest="runcron", default=False, help="start the background cron process"
    )

    parser.add_option(
        "-J", "--cronjob", action="store_true", dest="cronjob", default=False, help="identify cron-initiated command"
    )

    parser.add_option("-L", "--config", dest="config", default="", help="config file")

    parser.add_option("-F", "--profiler", dest="profiler_dir", default=None, help="profiler dir")

    parser.add_option(
        "-t",
        "--taskbar",
        action="store_true",
        dest="taskbar",
        default=False,
        help="use web2py gui and run in taskbar (system tray)",
    )

    parser.add_option("", "--nogui", action="store_true", default=False, dest="nogui", help="text-only, no GUI")

    msg = (
        "should be followed by a list of arguments to be passed to script, "
        "to be used with -S, -A must be the last option"
    )
    parser.add_option("-A", "--args", action="store", dest="args", default=None, help=msg)

    parser.add_option(
        "--no-banner", action="store_true", default=False, dest="nobanner", help="Do not print header banner"
    )

    msg = (
        "listen on multiple addresses: "
        '"ip1:port1:key1:cert1:ca_cert1;ip2:port2:key2:cert2:ca_cert2;..." '
        "(:key:cert:ca_cert optional; no spaces; IPv6 addresses must be in "
        "square [] brackets)"
    )
    parser.add_option("--interfaces", action="store", dest="interfaces", default=None, help=msg)

    msg = "runs web2py tests"
    parser.add_option("--run_system_tests", action="store_true", dest="run_system_tests", default=False, help=msg)

    msg = (
        "adds coverage reporting (needs --run_system_tests), "
        "python 2.7 and the coverage module installed. "
        "You can alter the default path setting the environmental "
        'var "COVERAGE_PROCESS_START". '
        "By default it takes gluon/tests/coverage.ini"
    )
    parser.add_option("--with_coverage", action="store_true", dest="with_coverage", default=False, help=msg)

    if "-A" in sys.argv:
        k = sys.argv.index("-A")
    elif "--args" in sys.argv:
        k = sys.argv.index("--args")
    else:
        k = len(sys.argv)
    sys.argv, other_args = sys.argv[:k], sys.argv[k + 1 :]
    (options, args) = parser.parse_args()
    options.args = [options.run] + other_args
    global_settings.cmd_options = options
    global_settings.cmd_args = args

    if options.gae:
        if not os.path.exists("app.yaml"):
            name = raw_input("Your GAE app name: ")
            content = open(os.path.join("examples", "app.example.yaml"), "rb").read()
            open("app.yaml", "wb").write(content.replace("yourappname", name))
        else:
            print "app.yaml alreday exists in the web2py folder"
        if not os.path.exists("gaehandler.py"):
            content = open(os.path.join("handlers", "gaehandler.py"), "rb").read()
            open("gaehandler.py", "wb").write(content)
        else:
            print "gaehandler.py alreday exists in the web2py folder"
        sys.exit(0)

    try:
        options.ips = list(
            set(  # no duplicates
                [
                    addrinfo[4][0]
                    for addrinfo in getipaddrinfo(socket.getfqdn())
                    if not is_loopback_ip_address(addrinfo=addrinfo)
                ]
            )
        )
    except socket.gaierror:
        options.ips = []

    if options.run_system_tests:
        run_system_tests(options)

    if options.quiet:
        capture = cStringIO.StringIO()
        sys.stdout = capture
        logger.setLevel(logging.CRITICAL + 1)
    else:
        logger.setLevel(options.debuglevel)

    if options.config[-3:] == ".py":
        options.config = options.config[:-3]

    if options.cronjob:
        global_settings.cronjob = True  # tell the world
        options.plain = True  # cronjobs use a plain shell
        options.nobanner = True
        options.nogui = True

    options.folder = os.path.abspath(options.folder)

    #  accept --interfaces in the form
    #  "ip1:port1:key1:cert1:ca_cert1;[ip2]:port2;ip3:port3:key3:cert3"
    #  (no spaces; optional key:cert indicate SSL)
    if isinstance(options.interfaces, str):
        interfaces = options.interfaces.split(";")
        options.interfaces = []
        for interface in interfaces:
            if interface.startswith("["):  # IPv6
                ip, if_remainder = interface.split("]", 1)
                ip = ip[1:]
                if_remainder = if_remainder[1:].split(":")
                if_remainder[0] = int(if_remainder[0])  # numeric port
                options.interfaces.append(tuple([ip] + if_remainder))
            else:  # IPv4
                interface = interface.split(":")
                interface[1] = int(interface[1])  # numeric port
                options.interfaces.append(tuple(interface))

    #  accepts --scheduler in the form
    #  "app:group1,group2,app2:group1"
    scheduler = []
    options.scheduler_groups = None
    if isinstance(options.scheduler, str):
        if ":" in options.scheduler:
            for opt in options.scheduler.split(","):
                scheduler.append(opt.split(":"))
            options.scheduler = ",".join([app[0] for app in scheduler])
            options.scheduler_groups = scheduler

    if options.numthreads is not None and options.minthreads is None:
        options.minthreads = options.numthreads  # legacy

    create_welcome_w2p()

    if not options.cronjob:
        # If we have the applications package or if we should upgrade
        if not os.path.exists("applications/__init__.py"):
            write_file("applications/__init__.py", "")

    return options, args
Esempio n. 10
0
def console():
    """ Defines the behavior of the console web2py execution """
    import optparse

    parser = optparse.OptionParser(
        usage='python %prog [options]',
        version=ProgramVersion,
        description='web2py Web Framework startup script.',
        epilog='''NOTE: unless a password is specified (-a 'passwd')
web2py will attempt to run a GUI to ask for it when starting the web server
(if not disabled with --nogui).''')

    parser.add_option('-i', '--ip',
                      default='127.0.0.1',
                      metavar='IP_ADDR', help=\
        'IP address of the server (e.g., 127.0.0.1 or ::1); ' \
        'Note: This value is ignored when using the --interfaces option')

    parser.add_option('-p', '--port',
                      default=8000,
                      type='int', metavar='NUM', help=\
        'port of server (%default); ' \
        'Note: This value is ignored when using the --interfaces option')

    parser.add_option('-G', '--GAE', dest='gae',
                      default=None,
                      metavar='APP_NAME', help=\
        'will create app.yaml and gaehandler.py and exit')

    parser.add_option('-a', '--password',
                      default='<ask>',
                      help=\
        'password to be used for administration ' \
        '(use "<recycle>" to reuse the last password), ' \
        'when no password is available the administrative ' \
        'interface will be disabled')

    parser.add_option('-c', '--ssl_certificate',
                      default=None,
                      metavar='FILE', help='server certificate file')

    parser.add_option('-k', '--ssl_private_key',
                      default=None,
                      metavar='FILE', help='server private key file')

    parser.add_option('--ca-cert', dest='ssl_ca_certificate',
                      default=None,
                      metavar='FILE', help='CA certificate file')

    parser.add_option('-d', '--pid_filename',
                      default='httpserver.pid',
                      metavar='FILE', help='server pid file (%default)')

    parser.add_option('-l', '--log_filename',
                      default='httpserver.log',
                      metavar='FILE', help='server log file (%default)')

    parser.add_option('-n', '--numthreads',
                      default=None,
                      type='int', metavar='NUM',
                      help='number of threads (deprecated)')

    parser.add_option('--minthreads',
                      default=None,
                      type='int', metavar='NUM',
                      help='minimum number of server threads')

    parser.add_option('--maxthreads',
                      default=None,
                      type='int', metavar='NUM',
                      help='maximum number of server threads')

    parser.add_option('-s', '--server_name',
                      default=socket.gethostname(),
                      help='web server name (%default)')

    parser.add_option('-q', '--request_queue_size',
                      default=5,
                      type='int', metavar='NUM',
                      help=\
        'max number of queued requests when server unavailable (%default)')

    parser.add_option('-o', '--timeout',
                      default=10,
                      type='int', metavar='SECONDS',
                      help='timeout for individual request (%default seconds)')

    parser.add_option('-z', '--shutdown_timeout',
                      default=None,
                      type='int', metavar='SECONDS',
                      help=\
        'timeout on server shutdown; this value is not used by ' \
        'Rocket web server')

    parser.add_option('--socket-timeout', dest='socket_timeout', # not needed
                      default=5,
                      type='int', metavar='SECONDS',
                      help='timeout for socket (%default seconds)')

    parser.add_option('-f', '--folder',
                      default=os.getcwd(), metavar='WEB2PY_DIR',
                      help='folder from which to run web2py')

    parser.add_option('-v', '--verbose',
                      default=False,
                      action='store_true',
                      help='increase --test and --run_system_tests verbosity')

    parser.add_option('-Q', '--quiet',
                      default=False,
                      action='store_true',
                      help='disable all output')

    parser.add_option('-e', '--errors_to_console', dest='print_errors',
                      default=False,
                      action='store_true',
                      help='log all errors to console')

    parser.add_option('-D', '--debug', dest='debuglevel',
                      default=30,
                      type='int',
                      metavar='LOG_LEVEL', help=\
        'set log level (0-100, 0 means all, 100 means none; ' \
        'default is %default)')

    parser.add_option('-S', '--shell',
                      default=None,
                      metavar='APPNAME', help=\
        'run web2py in interactive shell or IPython (if installed) with ' \
        'specified appname (if app does not exist it will be created). ' \
        'APPNAME like a/c/f?x=y (c,f and vars x,y optional)')

    parser.add_option('-B', '--bpython',
                      default=False,
                      action='store_true',
                      help=\
        'run web2py in interactive shell or bpython (if installed) with ' \
        'specified appname (if app does not exist it will be created). ' \
        'Use combined with --shell')

    parser.add_option('-P', '--plain',
                      default=False,
                      action='store_true',
                      help=\
        'only use plain python shell; should be used with --shell option')

    parser.add_option('-M', '--import_models',
                      default=False,
                      action='store_true',
                      help=\
        'auto import model files (default is %default); should be used ' \
        'with --shell option')

    parser.add_option('-R', '--run',
                      default='', # NOTE: used for sys.argv[0] if --shell
                      metavar='PYTHON_FILE', help=\
        'run PYTHON_FILE in web2py environment; ' \
        'should be used with --shell option')

    parser.add_option('-K', '--scheduler',
                      default=None,
                      metavar='APP_LIST', help=\
        'run scheduled tasks for the specified apps: expects a list of ' \
        'app names as app1,app2,app3 ' \
        'or a list of app:groups as app1:group1:group2,app2:group1 ' \
        '(only strings, no spaces allowed). NOTE: ' \
        'Requires a scheduler defined in the models')

    parser.add_option('-X', '--with-scheduler', dest='with_scheduler', # not needed
                      default=False,
                      action='store_true',
                      help=\
        'run schedulers alongside webserver, needs -K')

    parser.add_option('-T', '--test',
                      default=None,
                      metavar='TEST_PATH', help=\
        'run doctests in web2py environment; ' \
        'TEST_PATH like a/c/f (c,f optional)')

    parser.add_option('-C', '--cron', dest='extcron',
                      default=False,
                      action='store_true',
                      help=\
        'trigger a cron run and exit; usually used when invoked ' \
        'from a system crontab')

    parser.add_option('--softcron',
                      default=False,
                      action='store_true',
                      help=\
        'use software cron emulation instead of separate cron process, '\
        'needs -Y; NOTE: use of software cron emulation is strongly '
        'discouraged')

    parser.add_option('-Y', '--run-cron', dest='runcron',
                      default=False,
                      action='store_true',
                      help='start the background cron process')

    parser.add_option('-J', '--cronjob',
                      default=False,
                      action='store_true',
                      help='identify cron-initiated command')

    parser.add_option('-L', '--config',
                      default='',
                      help='config file')

    parser.add_option('-F', '--profiler', dest='profiler_dir',
                      default=None,
                      help='profiler dir')

    parser.add_option('-t', '--taskbar',
                      default=False,
                      action='store_true',
                      help='use web2py GUI and run in taskbar (system tray)')

    parser.add_option('--nogui',
                      default=False,
                      action='store_true',
                      help='do not run GUI')

    parser.add_option('-A', '--args',
                      default=None,
                      help=\
        'should be followed by a list of arguments to be passed to script, ' \
        'to be used with -S; NOTE: must be the last option because eat all ' \
        'remaining arguments')

    parser.add_option('--no-banner', dest='nobanner',
                      default=False,
                      action='store_true',
                      help='do not print header banner')

    parser.add_option('--interfaces',
                      default=None,
                      help=\
        'listen on multiple addresses: ' \
        '"ip1:port1:key1:cert1:ca_cert1;ip2:port2:key2:cert2:ca_cert2;..." ' \
        '(:key:cert:ca_cert optional; no spaces; IPv6 addresses must be in ' \
        'square [] brackets)')

    parser.add_option('--run_system_tests',
                      default=False,
                      action='store_true',
                      help='run web2py tests')

    parser.add_option('--with_coverage',
                      default=False,
                      action='store_true',
                      help=\
        'adds coverage reporting (should be used with --run_system_tests), ' \
        'needs Python 2.7+ and the coverage module installed. ' \
        'You can alter the default path setting the environment ' \
        'variable "COVERAGE_PROCESS_START" ' \
        '(by default it takes gluon/tests/coverage.ini)')

    if '-A' in sys.argv:
        k = sys.argv.index('-A')
    elif '--args' in sys.argv:
        k = sys.argv.index('--args')
    else:
        k = len(sys.argv)
    sys.argv, other_args = sys.argv[:k], sys.argv[k + 1:]
    (options, args) = parser.parse_args()
    # TODO: warn or error if args (should be no unparsed arguments)
    options.args = other_args

    if options.config.endswith('.py'):
        options.config = options.config[:-3]
    if options.config:
        # import options from options.config file
        try:
            # FIXME: avoid __import__
            options2 = __import__(options.config)
        except:
            die("cannot import config file %s" % options.config)
        for key in dir(options2):
            if hasattr(options, key):
                setattr(options, key, getattr(options2, key))

    # store in options.ips the list of server IP addresses
    try:
        options.ips = list(set(  # no duplicates
            [addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn())
             if not is_loopback_ip_address(addrinfo=addrinfo)]))
    except socket.gaierror:
        options.ips = []

    if options.cronjob:
        global_settings.cronjob = True  # tell the world
        options.plain = True    # cronjobs use a plain shell
        options.nobanner = True
        options.nogui = True

    # transform options.interfaces, in the form
    # "ip1:port1:key1:cert1:ca_cert1;[ip2]:port2;ip3:port3:key3:cert3"
    # (no spaces; optional key:cert:ca_cert indicate SSL), into
    # a list of tuples
    if options.interfaces:
        interfaces = options.interfaces.split(';')
        options.interfaces = []
        for interface in interfaces:
            if interface.startswith('['):
                # IPv6
                ip, if_remainder = interface.split(']', 1)
                ip = ip[1:]
                interface = if_remainder[1:].split(':')
                interface.insert(0, ip)
            else:
                # IPv4
                interface = interface.split(':')
            interface[1] = int(interface[1])  # numeric port
            options.interfaces.append(tuple(interface))

    # strip group infos from options.scheduler, in the form
    # "app:group1:group2,app2:group1", and put into a list of lists
    # in options.scheduler_groups
    if options.scheduler and ':' in options.scheduler:
        sg = options.scheduler_groups = []
        for awg in options.scheduler.split(','):
            sg.append(awg.split(':'))
        options.scheduler = ','.join([app[0] for app in sg])
    else:
        options.scheduler_groups = None

    if options.numthreads is not None and options.minthreads is None:
        options.minthreads = options.numthreads  # legacy

    copy_options = copy.deepcopy(options)
    copy_options.password = '******'
    global_settings.cmd_options = copy_options

    return options, args